diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e82a15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +debug_custom.json +debug.cfg +esp32.svd +debug.svd diff --git a/README.md b/README.md index df5511d..f49d5db 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,86 @@ -# ulisp-esp -A version of the Lisp programming language for ESP32-based boards. +# ulisp-esp32 -The "-comments" version is identical but includes comprehensive comments. +A (patched) version of the Lisp programming language for ESP32-based boards. +Heavily customized to fit my use case but most of the original remains. +For more about the original ulisp-esp see -For more information see: -http://www.ulisp.com/show?3M#esp-version +This is based off of uLisp 4.6. For the old patches (some of which don't work) for +uLisp 4.3a please see the [4.3a-old](https://github.com/dragoncoder047/ulisp-esp32/tree/4.3a-old) branch. + +> [!NOTE] +> This version includes (requires?) the [ESP32Servo](https://www.arduino.cc/reference/en/libraries/esp32servo/) library to get the analogWrite() and tone() functioning correctly. If you don't have it installed uLisp will compile but you won't have analogWrite() and tone(). + +New features, some care in editing required: +* Ability to add multiple (more than one) extension tables (using `calloc()`) *may not be portable to other platforms* +* Nonlocal exit: `(throw)` and `(catch)` (\*) +* Templating: backquote/unquote/unquote-splicing (\*) +* Macros: defmacro/macroexpand *no support for destructuring lambda lists yet* (\*) + +Copy-paste ready features (all in `extensions.hpp`): +* Gensym and intern +* Destructuring-bind +* Sizeof (not Common Lisp but useful nonetheless) + +Also included is David's bigint library and the example `(now)` function. + +Other patches: +* Deleted: load/save/autorunimage support +* Modified: garbage collect message +* Deleted: line-editor support +* Added: Auto-run contents of `main.lisp` (on microSD card) at startup +* Modified: SD-card functions now include filename in error messages +* Fixed: special forms don't need to call `checkargs()` because it is automatically called + +> [!CAUTION] +> If you are looking to use this patched version as a guide for adding any of the 3 starred (\*) features listed above, please use [this guide I prepared](https://dragoncoder047.github.io/pages/ulisp_howto.html) instead. There are many subtle changes in my patched version that are understandable to me, but will no doubt cause confusion for someone who is just copy-pasting my code. The aforementioned document is structured and designed to allow copy-pasting into vanilla uLisp without major problems arising. + +## `term.py` -- enhanced uLisp interface + +This provides a cleaner interface to use uLisp in compared to the stupid Arduino serial monitor. + +Dependencies: + +* A VT100-compliant terminal +* Python 3 +* [pyserial](https://pypi.org/project/pyserial/) (to communicate with your microcontroller) +* [prompt_toolkit](https://pypi.org/project/prompt-toolkit/) (to draw the interface) +* [Pygments](https://pypi.org/project/Pygments/) (for syntax highliting) + +To run: + +```bash +# use default port and baud (/dev/ttyUSB0 and 115200) +python3 term.py +# specify port and baud +python3 term.py -p COM3 -b 9600 +``` + +UI Overview: + +```txt +---------------------------------------------------- +| ^| ^| +| | | +| LISP | SERIAL | +| BUFFER | MONITOR | +| | | +| | | +| v| v| +|--------------------------------------------------| +|cmd> COMMAND AREA | +|--------------------------------------------------| +| STATUS BAR RIGHT STATUS | +| MEMORY USAGE LAST GC INFO | +---------------------------------------------------- +``` + +* Lisp Buffer: You can type Lisp code in here. +* Serial Monitor: This shows the output from the serial port. +* Command Area: You can type one-line Lisp commands in here, or you can type "special" commands (press ENTER to run them): + * `.reset`: Trips the RTS line of the serial port, to reset your microcontroller if it locks up and `~` doesn't work. + * `.run`: Sends the contents of the Lisp Buffer to the serial port, and then empty the Lisp Buffer. + * `.quit`: Closes the serial port, and exits from the application. +* Status Bar: Shows whether the program is running, waiting for input at the REPL, crashed because of an error, etc. +* Right Status: Doesn't do anything on its own, but if your program prints out something of the form `$!rs=foo!$`, it will hide that string in the Serial Monitor, and put `foo` in the Right Status area. This is useful if you want to monitor the state of a pin in a loop, and you don't want to overload the Serial Monitor with a barrage of text. +* Memory Usage: Shows the percentage of memory used by your program in a couple of different ways and also changes color depending on how much memory is used. This is updated after every garbage collection. +* Last GC Info: Shows how many garbage collections have been done since the start of the program, and how much was freed on the most recent GC. diff --git a/autotest.py b/autotest.py new file mode 100644 index 0000000..1b54a7f --- /dev/null +++ b/autotest.py @@ -0,0 +1,822 @@ +import serial +import time +import sys + +# copied from ulisp-builder cause I can't run Lisp + +TESTS = r""" + +(defvar errors nil) +(defvar crashes 0) + +(defun aeq (testname x y) + (unless (or (and (floatp x) + (floatp y) + (< (abs (- x y)) 0.000005)) + (equal x y)) + (let (b (assoc testname errors)) + (if b (incf (cdr b)) + (push (cons testname 1) errors))) + (format t "~a fail: expected ~a, got ~a~%" testname x y))) + +#| Symbols |# + +(aeq 'let 123 (let ((cat 123)) cat)) +(aeq 'let 79 (let ((ca% 79)) ca%)) +(aeq 'let 83 (let ((1- 83)) 1-)) +(aeq 'let 13 (let ((12a 13)) 12a)) +(aeq 'let 17 (let ((-1- 17)) -1-)) +(aeq 'let 66 (let ((abcdef 66)) abcdef)) +(aeq 'let 77 (let ((abcdefg 77)) abcdefg)) +(aeq 'let 88 (let ((abcdefgh 88)) abcdefgh)) +(aeq 'let 99 (let ((abcdefghi 99)) abcdefghi)) +(aeq 'let 1010 (let ((abcdefghij 1010)) abcdefghij)) +(aeq 'princ-to-string "ab9" (princ-to-string 'ab9)) +(aeq 'eq t (eq 'me 'me)) +(aeq 'eq t (eq 'fishcake 'fishcake)) +(aeq 'eq nil (eq 'fishcak 'fishca)) + +#| Arithmetic |# + +(aeq '* 9 (* -3 -3)) +(aeq '* 32580 (* 180 181)) +(aeq '* 1 (*)) +(aeq '+ 32767 (+ 32765 1 1)) +(aeq '+ 0 (+)) +(aeq '+ -2 (+ -1 -1)) +(aeq '- -4 (- 4)) +(aeq '- 0 (- 4 2 1 1)) +(aeq '/ 2 (/ 60 10 3)) +(aeq '1+ 2 (1+ 1)) +(aeq '1+ 0 (1+ -1)) +(aeq '1- 0 (1- 1)) + +#| Comparisons |# + +(aeq '< t (< -32768 32767)) +(aeq '< t (< -1 0)) +(aeq '< t (< 1 2 3 4)) +(aeq '< nil (< 1 2 2 4)) +(aeq '< t (<= 1 2 2 4)) +(aeq '< nil (<= 1 3 2 4)) +(aeq '< t (> 4 3 2 1)) +(aeq '< nil (> 4 2 2 1)) +(aeq '< t (>= 4 2 2 1)) +(aeq '< nil (>= 4 2 3 1)) +(aeq '< t (< 1)) +(aeq '< nil (< 1 3 2)) +(aeq '< nil (< -1 -2)) +(aeq '< nil (< 10 10)) +(aeq '<= t (<= 10 10)) +(aeq '= t (= 32767 32767)) +(aeq '>= t (>= 10 10)) +(aeq '>= nil (>= 9 10)) +(aeq '/= t (/= 1)) +(aeq '/= nil (/= 1 2 1)) +(aeq '/= nil (/= 1 2 3 1)) +(aeq '/= t (/= 1 2 3 4)) +(aeq 'plusp t (plusp 1)) +(aeq 'plusp nil (plusp 0)) +(aeq 'plusp nil (plusp -1)) +(aeq 'minusp nil (minusp 1)) +(aeq 'minusp nil (minusp 0)) +(aeq 'minusp t (minusp -1)) +(aeq 'zerop nil (zerop 1)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop -1)) +(aeq 'evenp nil (evenp 1)) +(aeq 'evenp t (evenp 0)) +(aeq 'evenp nil (evenp -1)) +(aeq 'oddp t (oddp 1)) +(aeq 'oddp nil (oddp 0)) +(aeq 'oddp t (oddp -1)) + +#| Maths functions |# + +(aeq 'abs 10 (abs 10)) +(aeq 'abs 10 (abs -10)) +(aeq 'max 45 (max 23 45)) +(aeq 'max -23 (max -23 -45)) +(aeq 'min 23 (min 23 45)) +(aeq 'min -45 (min -23 -45)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop 32767)) +(aeq 'mod 1 (mod 13 4)) +(aeq 'mod 3 (mod -13 4)) +(aeq 'mod -3 (mod 13 -4)) +(aeq 'mod -1 (mod -13 -4)) + +#| Number entry |# + +(aeq 'hex -1 #xFFFFFFFF) +(aeq 'hex 1 #x0001) +(aeq 'hex 4112 #x1010) +(aeq 'oct 511 #o777) +(aeq 'oct 1 #o1) +(aeq 'oct 65535 #o177777) +(aeq 'bin -1 #b11111111111111111111111111111111) +(aeq 'bin 10 #b1010) +(aeq 'bin 0 #b0) +(aeq 'hash 12 #'12) +(aeq 'hash 6 (funcall #'(lambda (x) (+ x 2)) 4)) + +#| Boolean |# + +(aeq 'and 7 (and t t 7)) +(aeq 'and nil (and t nil 7)) +(aeq 'or t (or t nil 7)) +(aeq 'or 1 (or 1 2 3)) +(aeq 'or nil (or nil nil nil)) +(aeq 'or 'a (or 'a 'b 'c)) +(aeq 'or 1 (let ((x 0)) (or (incf x)) x)) + +#| Bitwise |# + +(aeq 'logand -1 (logand)) +(aeq 'logand 170 (logand #xAA)) +(aeq 'logand 0 (logand #xAAAA #x5555)) +(aeq 'logior 0 (logior)) +(aeq 'logior 170 (logior #xAA)) +(aeq 'logior #xFFFF (logior #xAAAA #x5555)) +(aeq 'logxor 0 (logxor)) +(aeq 'logxor 170 (logior #xAA)) +(aeq 'logxor 255 (logxor #xAAAA #xAA55)) +(aeq 'lognot -43691 (lognot #xAAAA)) +(aeq 'ash 492 (ash 123 2)) +(aeq 'ash 65535 (ash #xFFFF 0)) +(aeq 'ash 16383 (ash #xFFFF -2)) +(aeq 'ash 262140 (ash #xFFFF 2)) +(aeq 'ash 8191 (ash #x7FFF -2)) +(aeq 'logbitp t (logbitp 0 1)) +(aeq 'logbitp t (logbitp 1000 -1)) +(aeq 'logbitp nil (logbitp 1000 0)) + +#| Tests |# + +(aeq 'atom t (atom nil)) +(aeq 'atom t (atom t)) +(aeq 'atom nil (atom '(1 2))) +(aeq 'consp nil (consp 'b)) +(aeq 'consp t (consp '(a b))) +(aeq 'consp nil (consp nil)) +(aeq 'listp nil (listp 'b)) +(aeq 'listp t (listp '(a b))) +(aeq 'listp t (listp nil)) +(aeq 'numberp t (numberp (+ 1 2))) +(aeq 'numberp nil (numberp 'b)) +(aeq 'numberp nil (numberp nil)) +(aeq 'symbolp t (symbolp 'b)) +(aeq 'symbolp nil (symbolp 3)) +(aeq 'symbolp t (symbolp nil)) +(aeq 'streamp nil (streamp 'b)) +(aeq 'streamp nil (streamp nil)) +(aeq 'boundp t (let (x) (boundp 'x))) +(aeq 'boundp nil (let (x) (boundp 'y))) + +#| cxr operations |# + +(aeq 'car 'a (car '(a b c))) +(aeq 'car nil (car nil)) +(aeq 'first 'a (first '(a b c))) +(aeq 'first nil (first nil)) +(aeq 'cdr 'b (cdr '(a . b))) +(aeq 'cdr 'b (car (cdr '(a b)))) +(aeq 'cdr nil (cdr nil)) +(aeq 'rest 'b (rest '(a . b))) +(aeq 'rest 'b (car (rest '(a b)))) +(aeq 'rest nil (rest nil)) +(aeq 'caaar 'a (caaar '(((a))))) +(aeq 'caaar 'nil (caaar nil)) +(aeq 'caadr 'b (caadr '(a (b)))) +(aeq 'caadr 'nil (caadr nil)) +(aeq 'caar 'a (caar '((a)))) +(aeq 'caar 'nil (caar nil)) +(aeq 'cadar 'c (cadar '((a c) (b)))) +(aeq 'cadar 'nil (cadar nil)) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'cadr 'b (cadr '(a b))) +(aeq 'second 'nil (second '(a))) +(aeq 'second 'b (second '(a b))) +(aeq 'cadr 'nil (cadr '(a))) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'third 'c (third '(a b c))) +(aeq 'third 'nil (third nil)) +(aeq 'cdaar 'b (car (cdaar '(((a b)) b c)))) +(aeq 'cdaar 'nil (cdaar nil)) +(aeq 'cdadr 'c (car (cdadr '(a (b c))))) +(aeq 'cdadr 'nil (cdadr nil)) +(aeq 'cdar 'b (car (cdar '((a b c))))) +(aeq 'cdar 'nil (cdar nil)) +(aeq 'cddar 'c (car (cddar '((a b c))))) +(aeq 'cddar 'nil (cddar nil)) +(aeq 'cdddr 'd (car (cdddr '(a b c d)))) +(aeq 'cdddr nil (car (cdddr '(a b c)))) +(aeq 'cddr 'c (car (cddr '(a b c)))) +(aeq 'cddr 'nil (cddr '(a))) + +#| List operations |# + +(aeq 'cons 'a (car (cons 'a 'b))) +(aeq 'cons nil (car (cons nil 'b))) +(aeq 'append 6 (length (append '(a b c) '(d e f)))) +(aeq 'append nil (append nil nil)) +(aeq 'append '(1 2 3 4 5 . 6) (append '(1 2 3) '(4 5 . 6))) +(aeq 'list nil (car (list nil))) +(aeq 'list 'a (car (list 'a 'b 'c))) +(aeq 'reverse 'c (car (reverse '(a b c)))) +(aeq 'reverse nil (reverse nil)) +(aeq 'length 0 (length nil)) +(aeq 'length 4 (length '(a b c d))) +(aeq 'length 2 (length '(nil nil))) +(aeq 'assoc nil (assoc 'b nil)) +(aeq 'assoc nil (assoc 'b '(nil nil))) +(aeq 'assoc '(b . 12) (assoc 'b '((a . 10) (b . 12)))) +(aeq 'assoc '(nil . 12) (assoc nil '((a . 10) (nil . 12)))) +(aeq 'assoc '(b) (assoc 'b '((a . 10) (b)))) +(aeq 'assoc '("three" . 3) (assoc "three" '(("one" . 1) ("two" . 2) ("three" . 3)) :test string=)) +(aeq 'member '(3 4) (member 3 '(1 2 3 4))) +(aeq 'member nil (member 5 '(1 2 3 4))) +(aeq 'member '(3 4) (member 3 '(1 2 3 4) :test eq)) +(aeq 'member '("three" "four") (member "three" '("one" "two" "three" "four") :test string=)) +(aeq 'member '("two" "three" "four") (member "three" '("one" "two" "three" "four") :test string<)) + +#| map operations |# + +(aeq 'mapc 2 (cadr (mapc + '(1 2 3 4)))) +(aeq 'mapc 10 (let ((x 0)) (mapc (lambda (y) (incf x y)) '(1 2 3 4)) x)) +(aeq 'mapcar '(1 4 9 16) (mapcar (lambda (x) (* x x)) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16) (mapcar * '(1 2 3 4) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16 25) (mapcar (lambda (x) (* x x)) '(1 2 3 4 5))) +(aeq 'mapcan '(1 4 2 5 3 6) (mapcan #'list '(1 2 3) '(4 5 6))) +(aeq 'mapcan '(1 3 2 4) (mapcan list '(1 2) '(3 4))) +(aeq 'mapcan '(1 5 9 2 6 10 3 7 11) (mapcan list '(1 2 3 4) '(5 6 7 8) '(9 10 11))) +(aeq 'mapcan '(1 2 3 . 4) (mapcan (lambda (x) x) '((1) (2) (3 . 4)))) +(aeq 'mapcan '(2 3 . 4) (mapcan (lambda (x) x) '(nil (2) (3 . 4)))) +(aeq 'maplist '(((1 2 3) 6 7 8) ((2 3) 7 8) ((3) 8)) (maplist #'cons '(1 2 3) '(6 7 8))) +(aeq 'maplist '(1 2 3) (mapl #'cons '(1 2 3) '(6 7 8))) +(aeq 'mapcan '(3 7 11) (mapcon (lambda (x) (when (eq (first x) (second x)) (list (car x)))) '(1 2 3 3 5 7 7 8 9 11 11))) + +#| let/let*/lambda |# + +(aeq 'let 7 (let ((x 7)) (let ((x 6) (y x)) y))) +(aeq 'let* 6 (let* ((x 7)) (let* ((x 6) (y x)) y))) +(aeq 'let t (let ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'let* t (let* ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'lambda 2 ((lambda (x y) (setq y x) y) 2 3)) +(aeq 'lambda 9 ((lambda (&rest x) (apply + x)) 2 3 4)) +(aeq 'lambda 8 ((lambda (x &optional (y 4)) (* x y)) 2)) +(aeq 'lambda 6 ((lambda (x &optional (y 4)) (* x y)) 2 3)) +(aeq 'lambda 6 ((lambda (x &optional y) (* x y)) 2 3)) +(aeq 'lambda 123 ((lambda (list) list) 123)) + +#| loops and control |# + +(aeq 'progn 8 (let ((x 6)) (progn (incf x) (incf x)))) +(aeq 'dotimes 21 (let ((x 6)) (dotimes (y 6 x) (setq x (+ x y))))) +(aeq 'dotimes 6 (let ((x 6)) (dotimes (y 6 y) (setq x (+ x y))))) +(aeq 'dotimes 0 (let ((x 6)) (dotimes (y 0 y) (setq x (+ x y))))) +(aeq 'dolist 6 (let ((x 0)) (dolist (y '(1 2 3) x) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3)) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3) y) (setq x (+ x y))))) +(aeq 'loop 6 (let ((x 0)) (loop (when (= x 6) (return x)) (incf x)))) +(aeq 'loop 6 (let ((x 0)) (loop (unless (< x 6) (return x)) (incf x)))) +(aeq 'return 'a (let ((a 7)) (loop (progn (return 'a))))) +(aeq 'return nil (loop (return))) +(aeq 'return 'a (let ((a 7)) (loop (progn (return 'a) nil)))) +(aeq 'do 2 (do* ((x 1 (1+ x)) (y 0 (1+ x))) ((= 3 y) x))) +(aeq 'do 3 (do ((x 1 (1+ x)) (y 0 (1+ x))) ((= 3 y) x))) +(aeq 'do 720 (do* ((n 6) (f 1 (* j f)) (j n (- j 1))) ((= j 0) f))) +(aeq 'do 720 (let ((n 6)) (do ((f 1 (* j f)) (j n (- j 1)) ) ((= j 0) f)))) +(aeq 'do 10 (do (a (b 1 (1+ b))) ((> b 10) a) (setq a b))) + +#| conditions |# + +(aeq 'if 3 (let ((a 2)) (if (= a 2) 3 4))) +(aeq 'if 4 (let ((a 2)) (if (= a 3) 3 4))) +(aeq 'if 4 (let ((a 3)) (if (= a 3) 4))) +(aeq 'if nil (let ((a 4)) (if (= a 3) 4))) +(aeq 'when 4 (let ((a 3)) (when (= a 3) 4))) +(aeq 'when nil (let ((a 2)) (when (= a 3) 4))) +(aeq 'unless nil (let ((a 3)) (unless (= a 3) 4))) +(aeq 'unless 4 (let ((a 2)) (unless (= a 3) 4))) +(aeq 'cond 8 (let ((a 2)) (cond ((= a 3) 7) ((= a 2) 8) (t 9)))) +(aeq 'cond 9 (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8) (9)))) +(aeq 'cond nil (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8)))) +(aeq 'cond 12 (car (cond ((evenp 3) (list (* 2 3))) ((list (* 3 4)))))) +(aeq 'case 222 (let ((j 1)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 333 (let ((j t)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 444 (let ((j 2)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) + +#| eval/funcall/apply |# + +(aeq 'funcall 10 (funcall + 1 2 3 4)) +(aeq 'funcall 'a (funcall car '(a b c d))) +(aeq 'funcall 3 (let ((x 0)) (funcall (lambda (y) (incf x y)) 3) x)) +(aeq 'apply 10 (apply + '(1 2 3 4))) +(aeq 'apply 13 (apply + 1 2 '(1 2 3 4))) +(aeq 'eval 10 (eval (list + 1 2 3 4))) +(aeq 'eval nil (eval nil)) +(aeq 'funcall 999 (let ((x 999)) (funcall (lambda (x) x) x))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (funcall fun (funcall fun x)))))) (funcall (x2 '1+) 2))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (fun (fun x)))))) ((x2 '1+) 2))) +(aeq 'apply 5 (let* ((my (lambda (x y) (+ x y))) (han '(my))) (apply (first han) '(2 3)))) + +#| in-place operations |# + +(aeq 'incf 5 (let ((x 0)) (+ (incf x) (incf x 2) (incf x -2)))) +(aeq 'decf -5 (let ((x 0)) (+ (decf x) (decf x 2) (decf x -2)))) +(aeq 'incf 12 (let ((x 0)) (+ (incf x 2) (incf x 2) (incf x 2)))) +(aeq 'incf 36 (let ((n 10)) (let* ((f1 (lambda () (incf n) n))) (+ (funcall f1) (funcall f1) (funcall f1))))) +(aeq 'setf 25 (let ((a 3) (b 4)) (setf a (* a 3) b (* b 4)) (+ a b))) +(aeq 'setf 9 (let ((a '(2 3))) (setf (car a) 6) (apply + a))) +(aeq 'setf 12 (let ((a '(2 3))) (setf (cdr a) '(6)) (apply * a))) +(aeq 'setf 220 (let ((a '(2 3 4))) (setf (nth 1 a) 11 (nth 2 a) 10) (apply * a))) + +#| recursion |# + +(aeq 'lambda 55 (let ((fib (lambda (n) (if (< n 3) 1 (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))) +(aeq 'lambda 5040 (let ((f (lambda (n) (if (= n 0) 1 (* n (f (- n 1))))))) (f 7))) +(aeq 'lambda 10 (let ((a 0)) (let ((f (lambda (n) (incf a n) (when (> n 0) (f (1- n)))))) (f 4)) a)) + +#| streams |# + +(aeq 'stream "" (with-output-to-string (s) (princ s s))) +(aeq 'stream "12 23 34" (with-output-to-string (st) (format st "~a ~a ~a" 12 23 34))) + +#| features |# + +(aeq 'features t (not (not (member :floating-point *features*)))) +(aeq 'features t (not (not (member :arrays *features*)))) + +#| printing |# + +(aeq 'princ "hello" (princ-to-string "hello")) +(aeq 'princ "hello \"David\"" (princ-to-string "hello \"David\"")) +(aeq 'prin1 "\"hello\"" (prin1-to-string "hello")) +(aeq 'prin1 "\"hello \\\"David\\\"\"" (prin1-to-string "hello \"David\"")) + +#| prettyprinting |# + +(aeq 'princ "hello" (princ-to-string "hello")) +(aeq 'pprint 10996 (let ((n 0) (st (with-output-to-string (str) (pprint aeq str)))) (dotimes (i (length st) n) (incf n (char-code (char st i)))))) + +#| documentation |# + +(aeq 'apropos '(progn apropos apropos-list unwind-protect) (apropos-list 'pro)) +(aeq 'apropos '(progn apropos apropos-list unwind-protect) (apropos-list "pro")) +(aeq 'documentation 7397 (let ((n 0)) (let ((st (documentation '?))) (dotimes (i (length st) n) (incf n (char-code (char st i))))))) + +#| format |# + +(aeq 'format "hello" (format nil "hello")) +(aeq 'format "Hello23Goodbye" (format nil "Hello~aGoodbye" 23)) +(aeq 'format " 17" (format nil "~5x" 23)) +(aeq 'format " 10111" (format nil "~6b" 23)) +(aeq 'format " 17 23 23 " (format nil "~5x ~5d ~5a" 23 23 23)) +(aeq 'format "00017 00023" (format nil "~5,'0x ~5,'0d" 23 23)) +(aeq 'format "01-45-07" (format nil "~2,'0d-~2,'0d-~2,'0d" 1 45 7)) +(aeq 'format "Hello42" (format nil "Hello~a" 42)) +(aeq 'format "[1,2,3]" (format nil "[~{~a~^,~}]" '(1 2 3))) +(aeq 'format "0003.14159" (format nil "~10,'0g" 3.14159)) +(aeq 'format "nil nil" (format nil "~a ~{ ~a ~} ~a" nil nil nil)) + +#| strings |# + +(aeq 'stringp t (stringp "hello")) +(aeq 'stringp nil (stringp 5)) +(aeq 'stringp nil (stringp '(a b))) +(aeq 'numberp nil (numberp "hello")) +(aeq 'atom t (atom "hello")) +(aeq 'consp nil (consp "hello")) +(aeq 'eq nil (eq "hello" "hello")) +(aeq 'eq t (let ((a "hello")) (eq a a))) +(aeq 'length 0 (length "")) +(aeq 'length 5 (length "hello")) +(aeq 'concatenate t (string= (concatenate 'string "A" "B") "AB")) +(aeq 'concatenate 3 (length (concatenate 'string "A" "BC"))) +(aeq 'concatenate 0 (length (concatenate 'string))) +(aeq 'concatenate "ABCD" (concatenate 'string "AB" "CD")) +(aeq 'concatenate "ABCDE" (concatenate 'string "AB" "CDE")) +(aeq 'concatenate "ABCDE" (concatenate 'string "ABC" "DE")) +(aeq 'concatenate "ABCDEF" (concatenate 'string "ABC" "DEF")) +(aeq 'string= nil (string= "cat" "cat ")) +(aeq 'string= t (string= "cat" "cat")) +(aeq 'string/= 3 (string/= "cat" "catx")) +(aeq 'string/= nil (string/= "cat" "cat")) +(aeq 'string/= nil (string/= "catt" "catt")) +(aeq 'string< nil (string< "cat" "cat")) +(aeq 'string<= 3 (string<= "cat" "cat")) +(aeq 'string< 3 (string< "cat" "cat ")) +(aeq 'string< 4 (string< "fish" "fish ")) +(aeq 'string> nil (string> "cat" "cat")) +(aeq 'string>= 3 (string>= "cat" "cat")) +(aeq 'string>= 5 (string>= "cattx" "cattx")) +(aeq 'string> 0 (string> "c" "a")) +(aeq 'string> 1 (string> "fc" "fa")) +(aeq 'string> 2 (string> "ffc" "ffa")) +(aeq 'string> 3 (string> "fffc" "fffa")) +(aeq 'string> 4 (string> "ffffc" "ffffa")) +(aeq 'string> 5 (string> "fffffc" "fffffa")) +(aeq 'string> nil (string< "fffffc" "fffffa")) +(aeq 'string "albatross" (string "albatross")) +(aeq 'string "x" (string #\x)) +(aeq 'string "cat" (string 'cat)) +(aeq 'string "albatross" (string 'albatross)) + + +#| subseq and search |# + +(aeq 'subseq "hello" (subseq "hellofromdavid" 0 5)) +(aeq 'subseq "fromdavid" (subseq "hellofromdavid" 5)) +(aeq 'subseq '(2 3 4) (subseq '(0 1 2 3 4) 2)) +(aeq 'subseq '(2) (subseq '(0 1 2 3 4) 2 3)) +(aeq 'subseq nil (subseq '() 0)) +(aeq 'search 4 (search "cat" "the cat sat on the mat")) +(aeq 'search 19 (search "mat" "the cat sat on the mat")) +(aeq 'search nil (search "hat" "the cat sat on the mat")) +(aeq 'search 1 (search '(1 2) '( 0 1 2 3 4))) +(aeq 'search nil (search '(2 1 2 3 4 5) '(2 1 2 3 4))) + +#| characters |# + +(aeq 'char-code 97 (char-code #\a)) +(aeq 'char-code 13 (char-code #\return)) +(aeq 'char-code 255 (char-code #\255)) +(aeq 'code-char #\return (code-char 13)) +(aeq 'code-char #\a (code-char 97)) +(aeq 'code-char #\255 (code-char 255)) +(aeq 'eq t (eq #\b #\b)) +(aeq 'eq nil (eq #\b #\B)) +(aeq 'numberp nil (numberp #\b)) +(aeq 'characterp t (characterp #\b)) +(aeq 'char #\o (char "hello" 4)) +(aeq 'char #\h (char "hello" 0)) +(aeq 'char "A" (princ-to-string (code-char 65))) +(aeq 'char "[#\\Bell]" (format nil "[~s]" (code-char 7))) +(aeq 'char "[#\\Return]" (format nil "[~s]" #\return)) +(aeq 'char "[#\\127]" (format nil "[~s]" #\127)) +(aeq 'char "[#\\255]" (format nil "[~s]" #\255)) + +#| read-from-string |# + +(aeq 'read-from-string 123 (read-from-string "123")) +(aeq 'read-from-string 144 (eval (read-from-string "((lambda (x) (* x x)) 12)"))) +(aeq 'read-from-string t (eval (read-from-string "(eq (+ 2 3) 5)"))) +(aeq 'read-from-string nil (read-from-string "()")) + +#| closures |# + +(aeq 'closure 'lex (let ((lex nil)) (funcall (let ((lex t)) (lambda () (if lex 'lex 'dyn)))))) +(aeq 'closure 103 (let* ((c 100) (two (lambda (d) (+ c d))) (one (lambda (c) (funcall two 3)))) (funcall one 1))) +(aeq 'closure 4 (let ((x 0)) (funcall (lambda (y) (incf x y)) 4) x)) +(aeq 'closure 0 (let ((x 0)) (funcall (let ((x 7)) (lambda (y) (setq x (+ x y) ))) 4) x)) +(aeq 'closure '(8 10 13 17) (let ((x 0) (clo (lambda () (let ((x 7)) (lambda (y) (incf x y)))))) (mapcar (funcall clo) '(1 2 3 4)))) +(aeq 'closure 3 (let ((y 0) (test (lambda (x) (+ x 1)))) (dotimes (x 3 y) (progn (test (+ x 2))) (incf y x)))) + +#| arrays |# + +(aeq 'array '(0 0) (array-dimensions #2a())) +(aeq 'array '(1 0) (array-dimensions #2a(()))) +(aeq 'array '(2 0) (array-dimensions #2a(() ()))) +(aeq 'array '(0) (array-dimensions (make-array '(0)))) +(aeq 'array '(0) (array-dimensions (make-array 0))) +(aeq 'array 1 (let ((a (make-array 3 :initial-element 0))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array '(3) :initial-element 0))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array '(2 3) :initial-element 0))) (incf (aref a 1 (+ 1 1))) (aref a 1 2))) +(aeq 'array 1 (let ((a (make-array '(2 3 2 2) :initial-element 0))) (incf (aref a 1 (+ 1 1) 1 1)) (aref a 1 2 1 1))) +(aeq 'array 10 (length (make-array 10 :initial-element 1))) + +#| bit arrays |# + +(aeq 'array '(0) (array-dimensions (make-array '(0) :element-type 'bit))) +(aeq 'array '(1 1) (array-dimensions (make-array '(1 1) :element-type 'bit))) +(aeq 'array 10 (length (make-array '(10) :element-type 'bit))) +(aeq 'array 10 (length (make-array 10 :element-type 'bit))) +(aeq 'array 1 (let ((a (make-array 3 :element-type 'bit))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array 3 :initial-element 0 :element-type 'bit))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 0 (let ((a (make-array 10 :element-type 'bit :initial-element 1))) (decf (aref a 4)) (aref a 4))) +(aeq 'array 1 (let ((a (make-array 40 :element-type 'bit :initial-element 0))) (incf (aref a 39)) (aref a 39))) +(aeq 'array 0 (let ((a (make-array 40 :element-type 'bit :initial-element 0))) (incf (aref a 39)) (decf (aref a 39)) (aref a 39))) + +#| repl |# + +(aeq 'repl 23 (read-from-string "23(2)")) +(aeq 'repl nil (read-from-string "()23")) +(aeq 'repl 23 (read-from-string "23\"Hi\"")) +(aeq 'repl "Hi" (read-from-string "\"Hi\"23")) +(aeq 'repl #\1 (read-from-string " #\\1\"Hi\"")) +(aeq 'repl "Hi" (read-from-string (format nil "\"Hi\"~a~a" #\# "*0101"))) + +#| equal |# + +(aeq 'equal t (equal '(1 2 3) '(1 2 3))) +(aeq 'equal t (equal '(1 2 (4) 3) '(1 2 (4) 3))) +(aeq 'equal nil (equal '(1 2 (4) 3) '(1 2 (4 nil) 3))) +(aeq 'equal t (equal "cat" "cat")) +(aeq 'equal nil (equal "cat" "Cat")) +(aeq 'equal t (equal 'cat 'Cat)) +(aeq 'equal t (equal 2 (+ 1 1))) +(aeq 'equal t (equal '("cat" "dog") '("cat" "dog"))) +(aeq 'equal nil (equal '("cat" "dog") '("cat" "dig"))) +(aeq 'equal nil (equal '("cat" "dog") '("cat" "Dog"))) + +#| keywords |# + +(aeq 'keywordp t (keywordp :led-builtin)) +(aeq 'keywordp nil (keywordp print)) +(aeq 'keywordp nil (keywordp nil)) +(aeq 'keywordp nil (keywordp 12)) +(aeq 'keywordp t (keywordp :fred)) +(aeq 'keywordp t (keywordp :initial-element)) +(aeq 'keywordp t (keywordp :element-type)) + +#| errors |# + +(aeq 'error 7 (let ((x 7)) (ignore-errors (setq x (/ 1 0))) x)) +(aeq 'error 5 (unwind-protect (+ 2 3) 13)) + +#| Printing floats |# + +(aeq 'print t (string= (princ-to-string 101.0) "101.0")) +(aeq 'print t (string= (princ-to-string 1010.0) "1010.0")) +(aeq 'print t (string= (princ-to-string 10100.0) "10100.0")) +(aeq 'print t (string= (princ-to-string 101000.0) "1.01e5")) +(aeq 'print t (string= (princ-to-string 1010000.0) "1.01e6")) +(aeq 'print t (string= (princ-to-string 1.01E7) "1.01e7")) +(aeq 'print t (string= (princ-to-string 1.01E8) "1.01e8")) +(aeq 'print t (string= (princ-to-string 7.0) "7.0")) +(aeq 'print t (string= (princ-to-string 70.0) "70.0")) +(aeq 'print t (string= (princ-to-string 700.0) "700.0")) +(aeq 'print t (string= (princ-to-string 7000.0) "7000.0")) +(aeq 'print t (string= (princ-to-string 70000.0) "70000.0")) +(aeq 'print t (string= (princ-to-string 700000.0) "7.0e5")) +(aeq 'print t (string= (princ-to-string 0.7) "0.7")) +(aeq 'print t (string= (princ-to-string 0.07) "0.07")) +(aeq 'print t (string= (princ-to-string 0.007) "0.007")) +(aeq 'print t (string= (princ-to-string 7.0E-4) "7.0e-4")) +(aeq 'print t (string= (princ-to-string 7.0E-5) "7.0e-5")) +(aeq 'print t (string= (princ-to-string 7.0E-6) "7.0e-6")) +(aeq 'print t (string= (princ-to-string 0.9) "0.9")) +(aeq 'print t (string= (princ-to-string 0.99) "0.99")) +(aeq 'print t (string= (princ-to-string 0.999) "0.999")) +(aeq 'print t (string= (princ-to-string 0.9999) "0.9999")) +(aeq 'print t (string= (princ-to-string 0.99999) "0.99999")) +(aeq 'print t (string= (princ-to-string 0.999999) "0.999999")) +(aeq 'print t (string= (princ-to-string 0.9999999) "1.0")) +(aeq 'print t (string= (princ-to-string 1.0) "1.0")) +(aeq 'print t (string= (princ-to-string 10.0) "10.0")) +(aeq 'print t (string= (princ-to-string 100.0) "100.0")) +(aeq 'print t (string= (princ-to-string 1000.0) "1000.0")) +(aeq 'print t (string= (princ-to-string 10000.0) "10000.0")) +(aeq 'print t (string= (princ-to-string 100000.0) "1.0e5")) +(aeq 'print t (string= (princ-to-string 9.0) "9.0")) +(aeq 'print t (string= (princ-to-string 90.0) "90.0")) +(aeq 'print t (string= (princ-to-string 900.0) "900.0")) +(aeq 'print t (string= (princ-to-string 9000.0) "9000.0")) +(aeq 'print t (string= (princ-to-string 90000.0) "90000.0")) +(aeq 'print t (string= (princ-to-string 900000.0) "9.0e5")) +(aeq 'print t (string= (princ-to-string -9.0) "-9.0")) +(aeq 'print t (string= (princ-to-string -90.0) "-90.0")) +(aeq 'print t (string= (princ-to-string -900.0) "-900.0")) +(aeq 'print t (string= (princ-to-string -9000.0) "-9000.0")) +(aeq 'print t (string= (princ-to-string -90000.0) "-90000.0")) +(aeq 'print t (string= (princ-to-string -900000.0) "-9.0e5")) +(aeq 'print t (string= (princ-to-string 1.0) "1.0")) +(aeq 'print t (string= (princ-to-string 1.01) "1.01")) +(aeq 'print t (string= (princ-to-string 1.001) "1.001")) +(aeq 'print t (string= (princ-to-string 1.0001) "1.0001")) +(aeq 'print t (string= (princ-to-string 1.00001) "1.00001")) +(aeq 'print t (string= (princ-to-string 1.000001) "1.0")) +(aeq 'print t (string= (princ-to-string 0.0012345678) "0.00123457")) +(aeq 'print t (string= (princ-to-string 1.2345678E-4) "1.23457e-4")) +(aeq 'print t (string= (princ-to-string 1234567.9) "1.23457e6")) +(aeq 'print t (string= (princ-to-string 1.2345679E7) "1.23457e7")) +(aeq 'print t (string= (princ-to-string 1.2E-9) "1.2e-9")) +(aeq 'print t (string= (princ-to-string 9.9E-8) "9.9e-8")) +(aeq 'print t (string= (princ-to-string 9.9999E-5) "9.9999e-5")) +(aeq 'print t (string= (princ-to-string 9.01) "9.01")) +(aeq 'print t (string= (princ-to-string 0.9999999) "1.0")) +(aeq 'print t (string= (princ-to-string 0.8999999) "0.9")) +(aeq 'print t (string= (princ-to-string 0.01) "0.01")) +(aeq 'print t (string= (princ-to-string 1.2345679) "1.23457")) +(aeq 'print t (string= (princ-to-string 12.345679) "12.3457")) +(aeq 'print t (string= (princ-to-string 123.45679) "123.457")) +(aeq 'print t (string= (princ-to-string 1234.5679) "1234.57")) +(aeq 'print t (string= (princ-to-string 12345.679) "12345.7")) +(aeq 'print t (string= (princ-to-string 123456.79) "1.23457e5")) +(aeq 'print t (string= (princ-to-string 1234567.9) "1.23457e6")) +(aeq 'print t (string= (princ-to-string 0.12345679) "0.123457")) +(aeq 'print t (string= (princ-to-string 0.012345679) "0.0123457")) +(aeq 'print t (string= (princ-to-string 0.0012345678) "0.00123457")) +(aeq 'print t (string= (princ-to-string 1.2345679E-4) "1.23457e-4")) + +#| Arithmetic |# + +(aeq '= t (= (- 4 2 1 1) 0)) +(aeq '* 9 (* -3 -3)) +(aeq '* 32580 (* 180 181)) +(aeq '* 1 (*)) +(aeq '* t (string= "-4.29497e9" (princ-to-string (* 2 -2147483648)))) +(aeq '* -2147483648 (* 2 -1073741824)) +(aeq '+ 32767 (+ 32765 1 1)) +(aeq '+ 0 (+)) +(aeq '+ -2 (+ -1 -1)) +(aeq '- -4 (- 4)) +(aeq '/ 2 (/ 60 10 3)) +(aeq '1+ 2.5 (1+ 1.5)) +(aeq '1+ 2147483647 (1+ 2147483646)) +(aeq '1+ t (string= "2.14748e9" (princ-to-string (1+ 2147483647)))) +(aeq '1- 0.5 (1- 1.5)) +(aeq '1- -2147483648 (1- -2147483647)) +(aeq '1- t (string= "-2.14748e9" (princ-to-string (1- -2147483648)))) + +#| Arithmetic |# + +(aeq '/ 1.75 (/ 3.5 2)) +(aeq '/ 1.75 (/ 3.5 2.0)) +(aeq '/ 0.0625 (/ 1 16)) +(aeq '/ 0.0625 (/ 1.0 16)) +(aeq '/ 0.0625 (/ 1 16.0)) +(aeq '/ 2 (/ 12 2 3)) +(aeq '/ 2.0 (/ 12.0 2 3)) +(aeq '/ 2.0 (/ 12 2.0 3)) +(aeq '/ 2.0 (/ 12 2 3.0)) +(aeq '/ 1 (/ 1)) +(aeq '/ t (string= "2.14748e9" (princ-to-string (/ -2147483648 -1)))) +(aeq '/ 2147483647 (/ -2147483647 -1)) +(aeq '/ 0.5 (/ 2)) +(aeq '* 1.0 (* 0.0625 16)) +(aeq '* 1.0 (* 0.0625 16.0)) + +#| Place |# + +(aeq 'incf 5.4 (let ((x 0)) (+ (incf x) (incf x 0.2) (incf x 2)))) +(aeq 'decf -5.4 (let ((x 0)) (+ (decf x) (decf x 0.2) (decf x 2)))) +(aeq 'incf 30.6 (let ((n 10)) (let* ((f1 (lambda () (incf n 0.1) n))) (+ (funcall f1) (funcall f1) (funcall f1))))) +(aeq 'setf "hellx" (let ((s "hello")) (setf (char s 4) #\x) s)) + +#| Comparisons |# + +(aeq '< t (< 1 2 3 4)) +(aeq '< nil (< 1 2 3 2)) +(aeq '< t (< 1.0 2 3 4)) +(aeq '< nil (< 1 2 3 2)) +(aeq '< t (< 1.0 1.001 3 4)) +(aeq '< nil (< 1.001 1.0 3 4)) +(aeq '< t (< 1.001 1.002 1.003 1.004)) +(aeq '< t (< 1. 2. 3. 4.)) +(aeq '< nil (< 1. 2. 2. 4.)) +(aeq '< t (<= 1. 2. 2. 4.)) +(aeq '< nil (<= 1. 3. 2. 4.)) +(aeq '< t (> 4. 3. 2. 1.)) +(aeq '< nil (> 4. 2. 2. 1.)) +(aeq '< t (>= 4. 2. 2. 1.)) +(aeq '< nil (>= 4. 2. 3. 1.)) +(aeq '/= t (= 1. 1. 1. 1.)) +(aeq '/= nil (= 1. 1. 2. 1.)) +(aeq '/= nil (/= 1. 2. 3. 1.)) +(aeq '/= t (/= 1. 2. 3. 4.)) + +#| Transcendental |# + +(aeq 'sin 0.84147096 (sin 1)) +(aeq 'sin 0.0 (sin 0)) +(aeq 'sin 0.84147096 (sin 1.0)) +(aeq 'sin 0.0 (sin 0.0)) +(aeq 'cos 0.540302 (cos 1)) +(aeq 'cos 0.540302 (cos 1.0)) +(aeq 'tan 1.55741 (tan 1)) +(aeq 'tan 1.55741 (tan 1.0)) +(aeq 'asin 1.5707964 (asin 1)) +(aeq 'asin 1.5707964 (asin 1)) +(aeq 'asin 0.0 (asin 0)) +(aeq 'asin 0.0 (asin 0.0)) +(aeq 'acos 0.0 (acos 1)) +(aeq 'acos 0.0 (acos 1.0)) +(aeq 'acos 1.0471976 (acos 0.5)) +(aeq 'atan 0.4636476 (atan 0.5)) +(aeq 'atan 0.110657 (atan 1 9)) +(aeq 'atan 0.049958397 (atan 1 20)) +(aeq 'atan 0.785398 (atan 1 1)) +(aeq 'atan 0.785398 (atan .5 .5))x +(aeq 'sinh 1.1752 (sinh 1)) +(aeq 'sinh 1.1752 (sinh 1.0)) +(aeq 'sinh 0.0 (sinh 0)) +(aeq 'sinh 0.0 (sin 0.0)) +(aeq 'cosh 1.5430807 (cosh 1)) +(aeq 'cosh 1.5430807 (cosh 1.0)) +(aeq 'tanh 0.7615942 (tanh 1)) +(aeq 'tanh 0.7615942 (tanh 1.0)) + +#| Rounding |# + +(aeq 'truncate 3 (truncate 10 3)) +(aeq 'truncate 3 (truncate 3.3333333)) +(aeq 'ceiling 4 (ceiling 10 3)) +(aeq 'ceiling 4 (ceiling 3.3333333)) +(aeq 'round 3 (round 10 3)) +(aeq 'round 3 (round 3.3333333)) +(aeq 'floor 3 (floor 10 3)) +(aeq 'floor 3 (floor 3.3333333)) +(aeq 'truncate -3 (truncate -10 3)) +(aeq 'truncate -3 (truncate -3.3333333)) +(aeq 'ceiling -3 (ceiling -10 3)) +(aeq 'ceiling -3 (ceiling -3.3333333)) +(aeq 'round -3 (round -10 3)) +(aeq 'round -3 (round -3.3333333)) +(aeq 'floor -4 (floor -10 3)) +(aeq 'floor -4 (floor -3.3333333)) +(aeq 'abs 10.0 (abs 10.0)) +(aeq 'abs 10.0 (abs -10.0)) +(aeq 'abs t (string= "2.14748e9" (princ-to-string (abs -2147483648)))) +(aeq 'abs 2147483647 (abs -2147483647)) +(aeq 'mod 1.0 (mod 13.0 4)) +(aeq 'mod 3.0 (mod -13.0 4)) +(aeq 'mod -3.0 (mod 13.0 -4)) +(aeq 'mod -1.0 (mod -13.0 -4)) +(aeq 'mod -3.0 (mod 13.0 -4)) +(aeq 'mod 1.0 (mod -12.5 1.5)) +(aeq 'mod 0.5 (mod 12.5 1.5)) + +#| Log and exp |# + +(aeq 'exp 2.7182818 (exp 1)) +(aeq 'exp 2.7182818 (exp 1.0)) +(aeq 'exp 0.36787945 (exp -1)) +(aeq 'exp 0.36787945 (exp -1.0)) +(aeq 'exp 0.36787945 (exp -1.0)) +(aeq 'log 0.0 (log 1.0)) +(aeq 'log 4.0 (log 16 2)) +(aeq 'log 4.0 (log 16.0 2)) +(aeq 'log 4.0 (log 16 2.0)) +(aeq 'log 4.0 (log 16.0 2.0)) +(aeq 'log 1.0 (log 2 2)) +(aeq 'log 1.0 (log 2.5 2.5)) +(aeq 'log 2.3025852 (log 10)) +(aeq 'log 2.3025852 (log 10)) +(aeq 'expt 1024 (expt 2 10)) +(aeq 'expt 1024.0 (expt 2.0 10.0)) +(aeq 'expt 1073741824 (expt 2 30)) +(aeq 'expt t (string= "2.14748e9" (princ-to-string (expt 2 31)))) +(aeq 'expt t (string= "4.29497e9" (princ-to-string (expt 2 32)))) +(aeq 'expt 1024 (expt -2 10)) +(aeq 'expt -2048 (expt -2 11)) + +#| Tests |# + +(aeq 'floatp nil (floatp 1)) +(aeq 'floatp nil (floatp nil)) +(aeq 'floatp t (floatp 2.3)) +(aeq 'integerp t (integerp 1)) +(aeq 'integerp nil (integerp nil)) +(aeq 'integerp nil (integerp 2.3)) + +#| error checks |# + +(aeq 'dolist nothing (ignore-errors (dolist 12 (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist () (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist (x) (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist (x nil x x) (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes 12 (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes () (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes (x) (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes (x 1 x x) (print x)))) +(aeq 'for-millis nothing (ignore-errors (for-millis 12 (print 12)))) +(aeq 'for-millis nothing (ignore-errors (for-millis (12 12) (print 12)))) +(aeq 'push nothing (ignore-errors (let ((a #*00000000)) (push 1 (aref a 1)) a))) +(aeq 'setf nothing (ignore-errors (let ((s "hello")) (setf (char s 5) #\x) s))) +(aeq 'setf nothing (ignore-errors (let ((s "hello")) (setf (char s 20) #\x) s))) + +#| errors |# + +(format t "~%Failing tests:~%~{~a~%~}~%~a tests crashed." errors crashes) + +""" + + +def talk(string: str, port: serial.Serial, ttw: float = 0.1): + port.reset_output_buffer() + port.write(string.encode()) + time.sleep(ttw) + text = port.read(port.in_waiting).decode().replace("\r\n", "\n") + sys.stdout.write(text) + return text + + +def test(): + port = serial.Serial("/dev/ttyUSB0", 115200) + # reset the board + port.dtr = False + port.dtr = True + talk("", port, 5.0) + + for line in TESTS.split("\n"): + if line and line.startswith("("): + text = talk(line, port) + if "Error:" in text or "Error in" in text: + talk("(incf crashes)", port) + + +test() diff --git a/bignums.hpp b/bignums.hpp new file mode 100644 index 0000000..89a1f7c --- /dev/null +++ b/bignums.hpp @@ -0,0 +1,596 @@ +/* + Arbitrary Precision uLisp Extension - Version 1 - 11th April 2023 + See http://forum.ulisp.com/t/a-ulisp-extension-for-arbitrary-precision-arithmetic/1183 +*/ +#include +#include "ulisp.hpp" + +#define MAX_VAL ((uint64_t)0xFFFFFFFF) +#define int_to_bignum(x) (cons(number(x), NULL)) +enum { + SMALLER = -1, + EQUAL = 0, + LARGER = 1 +}; + +// Forward references +object* do_operator(object* bignum1, object* bignum2, uint32_t (*op)(uint32_t, uint32_t)); +uint32_t op_ior(uint32_t, uint32_t); +int bignum_cmp(object* bignum1, object* bignum2); + + +// Internal utility functions + +/* + maybe_gc - Does a garbage collection if less than 1/16 workspace remains. +*/ +void maybe_gc(object* arg, object* env) { + if (Freespace <= WORKSPACESIZE >> 4) gc(arg, env); +} + +/* + checkbignum - checks argument is cons. + It makes the other routines simpler if we don't allow a null list. +*/ +object* checkbignum(object* b) { + if (!consp(b)) error(PSTR("argument is not a bignum"), b); + return b; +} + +/* + bignum_zerop - Tests whether a bignum is zero, allowing for possible trailing zeros. +*/ +bool bignum_zerop(object* bignum) { + while (bignum != NULL) { + if (checkinteger(car(bignum)) != 0) return false; + bignum = cdr(bignum); + } + return true; +} + +/* + bignum_normalise - Destructively removes trailing zeros. +*/ +object* bignum_normalise(object* bignum) { + object* result = bignum; + object* last = bignum; + while (bignum != NULL) { + if (checkinteger(car(bignum)) != 0) last = bignum; + bignum = cdr(bignum); + } + cdr(last) = NULL; + return result; +} + +/* + copylist - Returns a copy of a list. +*/ +object* copylist(object* arg) { + object* result = cons(NULL, NULL); + object* ptr = result; + while (arg != NULL) { + cdr(ptr) = cons(car(arg), NULL); + ptr = cdr(ptr); + arg = cdr(arg); + } + return cdr(result); +} + +/* + upshift_bit - Destructively shifts a bignum up one bit; ie multiplies by 2. +*/ +void upshift_bit(object* bignum) { + uint32_t now = (uint32_t)checkinteger(car(bignum)); + car(bignum) = number(now << 1); + while (cdr(bignum) != NULL) { + uint32_t next = (uint32_t)checkinteger(car(cdr(bignum))); + car(cdr(bignum)) = number((next << 1) | (now >> 31)); + now = next; + bignum = cdr(bignum); + } + if (now >> 31 != 0) cdr(bignum) = cons(number(now >> 31), NULL); +} + +/* + downshift_bit - Destructively shifts a bignum down one bit; ie divides by 2. +*/ +void downshift_bit(object* bignum) { + uint32_t now = (uint32_t)checkinteger(car(bignum)); + while (cdr(bignum) != NULL) { + uint32_t next = (uint32_t)checkinteger(car(cdr(bignum))); + car(bignum) = number((now >> 1) | (next << 31)); + now = next; + bignum = cdr(bignum); + } + car(bignum) = number(now >> 1); +} + +/* + bignum_from_int - Converts a 64-bit integer to a bignum and returns it. +*/ +object* bignum_from_int(uint64_t n) { + uint32_t high = n >> 32; + if (high == 0) return cons(number(n), NULL); + return cons(number(n), cons(number(high), NULL)); +} + +/* + bignum_add - Performs bignum1 + bignum2. +*/ +object* bignum_add(object* bignum1, object* bignum2) { + object* result = cons(NULL, NULL); + object* ptr = result; + int carry = 0; + while (!(bignum1 == NULL && bignum2 == NULL)) { + uint64_t tmp1 = 0, tmp2 = 0, tmp; + if (bignum1 != NULL) { + tmp1 = (uint64_t)(uint32_t)checkinteger(first(bignum1)); + bignum1 = cdr(bignum1); + } + if (bignum2 != NULL) { + tmp2 = (uint64_t)(uint32_t)checkinteger(first(bignum2)); + bignum2 = cdr(bignum2); + } + tmp = tmp1 + tmp2 + carry; + carry = (tmp > MAX_VAL); + cdr(ptr) = cons(number(tmp & MAX_VAL), NULL); + ptr = cdr(ptr); + } + if (carry != 0) { + cdr(ptr) = cons(number(carry), NULL); + } + return cdr(result); +} + +/* + bignum_sub - Performs bignum1 = bignum1 - bignum2. +*/ +object* bignum_sub(object* bignum1, object* bignum2) { + object* result = cons(NULL, NULL); + object* ptr = result; + int borrow = 0; + while (!(bignum1 == NULL && bignum2 == NULL)) { + uint64_t tmp1, tmp2, res; + if (bignum1 != NULL) { + tmp1 = (uint64_t)(uint32_t)checkinteger(first(bignum1)) + (MAX_VAL + 1); + bignum1 = cdr(bignum1); + } else tmp1 = (MAX_VAL + 1); + if (bignum2 != NULL) { + tmp2 = (uint64_t)(uint32_t)checkinteger(first(bignum2)) + borrow; + bignum2 = cdr(bignum2); + } else tmp2 = borrow; + res = tmp1 - tmp2; + borrow = (res <= MAX_VAL); + cdr(ptr) = cons(number(res & MAX_VAL), NULL); + ptr = cdr(ptr); + } + return cdr(result); +} + +/* + bignum_mul - Performs bignum1 * bignum2. +*/ +object* bignum_mul(object* bignum1, object* bignum2, object* env) { + object* result = int_to_bignum(0); + object* arg2 = bignum2; + int i = 0, j; + while (bignum1 != NULL) { + bignum2 = arg2; + j = 0; + while (bignum2 != NULL) { + uint64_t n = (uint64_t)(uint32_t)checkinteger(first(bignum1)) * (uint64_t)(uint32_t)checkinteger(first(bignum2)); + object* tmp; + if (n > MAX_VAL) tmp = cons(number(n), cons(number(n >> (uint64_t)32), NULL)); + else tmp = cons(number(n), NULL); + for (int m = i + j; m > 0; m--) push(number(0), tmp); // upshift i+j words + result = bignum_add(result, tmp); + bignum2 = cdr(bignum2); + j++; + maybe_gc(result, env); + } + bignum1 = cdr(bignum1); + i++; + } + return result; +} + +/* + bignum_div - Performs bignum1 / bignum2 and returns the list (quotient remainder). + First we normalise the denominator, and then do bitwise subtraction. + We need to do gcs in the main loops, while preserving the temporary lists on the GCStack. +*/ +object* bignum_div(object* bignum1, object* bignum2, object* env) { + object* current = int_to_bignum(1); + object* denom = copylist(bignum2); + while (bignum_cmp(denom, bignum1) != LARGER) { + push(number(0), current); + push(number(0), denom); // upshift current and denom 1 word + protect(current); + maybe_gc(denom, env); + unprotect(); + } + + object* result = int_to_bignum(0); + object* remainder = copylist(bignum1); + while (!bignum_zerop(current)) { + if (bignum_cmp(remainder, denom) != SMALLER) { + remainder = bignum_sub(remainder, denom); + result = do_operator(result, current, op_ior); + } + downshift_bit(current); + downshift_bit(denom); + protect(current); + protect(remainder); + protect(denom); + maybe_gc(result, env); + unprotect(); + unprotect(); + unprotect(); + } + return cons(result, cons(remainder, NULL)); +} + +/* + bignum_cmp - Compares two bignums and returns LARGER (b1>b2), EQUAL (b1=b2), or SMALLER (b1 b2) state = LARGER; + else if (b1 < b2) state = SMALLER; + } + return state; +} + +uint32_t op_and(uint32_t a, uint32_t b) { + return a & b; +}; +uint32_t op_ior(uint32_t a, uint32_t b) { + return a | b; +}; +uint32_t op_xor(uint32_t a, uint32_t b) { + return a ^ b; +}; + +/* + do_operator - Returns the result of performing a logical operation on two bignums. +*/ +object* do_operator(object* bignum1, object* bignum2, uint32_t (*op)(uint32_t, uint32_t)) { + object* result = cons(NULL, NULL); + object* ptr = result; + uint32_t tmp1 = 0, tmp2 = 0; + while (!(bignum1 == NULL && bignum2 == NULL)) { + if (bignum1 != NULL) { + tmp1 = (uint32_t)checkinteger(first(bignum1)); + bignum1 = cdr(bignum1); + } + if (bignum2 != NULL) { + tmp2 = (uint32_t)checkinteger(first(bignum2)); + bignum2 = cdr(bignum2); + } + cdr(ptr) = cons(number(op(tmp1, tmp2)), NULL); + ptr = cdr(ptr); + } + return cdr(result); +} + +// Lisp functions + +/* + ($bignum int) + Converts an integer to a bignum and returns it. +*/ +object* fn_BIGbignum(object* args, object* env) { + (void)env; + return int_to_bignum(checkinteger(first(args))); +} + +/* + ($integer bignum) + Converts a bignum to an integer and returns it. +*/ +object* fn_BIGinteger(object* args, object* env) { + (void)env; + object* bignum = checkbignum(first(args)); + bignum = bignum_normalise(bignum); + uint32_t i = checkinteger(first(bignum)); + if (cdr(bignum) != NULL || i > 0x7FFFFFFF) error2(PSTR("bignum too large to convert to an integer")); + return number(i); +} + +/* + ($bignum-string bignum [base]) + Converts a bignum to a string in base 10 (default) or 16 and returns it. + Base 16 is trivial. For base 10 we get remainders mod 1000000000 and then print those. +*/ +object* fn_BIGbignumstring(object* args, object* env) { + (void)env; + object* bignum = copylist(checkbignum(first(args))); + int b = 10; + uint32_t p; + args = cdr(args); + if (args != NULL) b = checkinteger(car(args)); + object* list = NULL; + if (b == 16) { + p = 0x10000000; + while (bignum != NULL) { + push(car(bignum), list); + bignum = cdr(bignum); + } + } else if (b == 10) { + p = 100000000; + object* base = cons(number(p * 10), NULL); + while (!bignum_zerop(bignum)) { + protect(bignum); + protect(base); + protect(list); + object* result = bignum_div(bignum, base, env); + unprotect(); + unprotect(); + unprotect(); + object* remainder = car(second(result)); + bignum = first(result); + push(remainder, list); + } + } else error2(PSTR("only base 10 or 16 supported")); + bool lead = false; + object* obj = newstring(); + object* tail = obj; + while (list != NULL) { + uint32_t i = car(list)->integer; + for (uint32_t d = p; d > 0; d = d / b) { + uint32_t j = i / d; + if (j != 0 || lead || d == 1) { + char ch = (j < 10) ? j + '0' : j + 'W'; + lead = true; + buildstring(ch, &tail); + } + i = i - j * d; + } + list = cdr(list); + } + return obj; +} + +/* + ($string-bignum string [base]) + Converts a string in the specified base, 10 (default) or 16, to a bignum and returns it. +*/ +object* fn_BIGstringbignum(object* args, object* env) { + (void)env; + object* string = first(args); + if (!stringp(string)) error(notastring, string); + int b = 10; + args = cdr(args); + if (args != NULL) b = checkinteger(car(args)); + if (b != 10 && b != 16) error2(PSTR("only base 10 or 16 supported")); + object* base = int_to_bignum(b); + object* result = int_to_bignum(0); + object* form = (object*)string->name; + while (form != NULL) { + int chars = form->chars; + for (int i = (sizeof(int) - 1) * 8; i >= 0; i = i - 8) { + char ch = chars >> i & 0xFF; + if (!ch) break; + int d = digitvalue(ch); + if (d >= b) error(PSTR("illegal character in bignum"), character(ch)); + protect(result); + protect(base); + result = bignum_mul(result, base, env); + unprotect(); + unprotect(); + result = bignum_add(result, cons(number(d), NULL)); + } + form = car(form); + } + return result; +} + +/* + ($zerop bignum) + Tests whether a bignum is zero, allowing for trailing zeros. +*/ +object* fn_BIGzerop(object* args, object* env) { + (void)env; + return bignum_zerop(checkbignum(first(args))) ? tee : nil; +} + +/* + ($+ bignum1 bignum2) + Adds two bignums and returns the sum as a new bignum. +*/ +object* fn_BIGadd(object* args, object* env) { + (void)env; + return bignum_add(checkbignum(first(args)), checkbignum(second(args))); +} + +/* + ($- bignum1 bignum2) + Subtracts two bignums and returns the difference as a new bignum. +*/ +object* fn_BIGsub(object* args, object* env) { + (void)env; + return bignum_sub(checkbignum(first(args)), checkbignum(second(args))); +} + +/* + ($* bignum1 bignum2) + Multiplies two bignums and returns the product as a new bignum. +*/ +object* fn_BIGmul(object* args, object* env) { + return bignum_mul(checkbignum(first(args)), checkbignum(second(args)), env); +} + +/* + ($/ bignum1 bignum2) + Divides two bignums and returns the quotient as a new bignum. +*/ +object* fn_BIGdiv(object* args, object* env) { + return first(bignum_div(checkbignum(first(args)), checkbignum(second(args)), env)); +} + +/* + ($mod bignum1 bignum2) + Divides two bignums and returns the remainder as a new bignum. +*/ +object* fn_BIGmod(object* args, object* env) { + return second(bignum_div(checkbignum(first(args)), checkbignum(second(args)), env)); +} + +// Comparisons +/* + ($= bignum1 bignum2) + Returns t if the two bignums are equal. +*/ +object* fn_BIGequal(object* args, object* env) { + (void)env; + return (bignum_cmp(checkbignum(first(args)), checkbignum(second(args))) == EQUAL) ? tee : nil; +} + +/* + ($< bignum1 bignum2) + Returns t if bignum1 is less than bignum2. +*/ +object* fn_BIGless(object* args, object* env) { + (void)env; + return (bignum_cmp(checkbignum(first(args)), checkbignum(second(args))) == SMALLER) ? tee : nil; +} + +/* + ($> bignum1 bignum2) + Returns t if bignum1 is greater than bignum2. +*/ +object* fn_BIGgreater(object* args, object* env) { + (void)env; + return (bignum_cmp(checkbignum(first(args)), checkbignum(second(args))) == LARGER) ? tee : nil; +} + +// Bitwise logical operations + +/* + ($logand bignum1 bignum2) + Returns the logical AND of two bignums. +*/ +object* fn_BIGlogand(object* args, object* env) { + (void)env; + return bignum_normalise(do_operator(checkbignum(first(args)), checkbignum(second(args)), op_and)); +} + +/* + ($logior bignum1 bignum2) + Returns the logical inclusive OR of two bignums. +*/ +object* fn_BIGlogior(object* args, object* env) { + (void)env; + return bignum_normalise(do_operator(checkbignum(first(args)), checkbignum(second(args)), op_ior)); +} + +/* + ($logxor bignum1 bignum2) + Returns the logical exclusive OR of two bignums. +*/ +object* fn_BIGlogxor(object* args, object* env) { + (void)env; + return bignum_normalise(do_operator(checkbignum(first(args)), checkbignum(second(args)), op_xor)); +} + +/* + ($ash bignum shift) + Returns bignum shifted by shift bits; positive means left. +*/ +object* fn_BIGash(object* args, object* env) { + (void)env; + object* bignum = copylist(checkbignum(first(args))); + int shift = checkinteger(second(args)); + for (int i = 0; i < shift; i++) upshift_bit(bignum); + for (int i = 0; i < -shift; i++) downshift_bit(bignum); + return bignum_normalise(bignum); +} + +// Symbol names +const char stringBIGbignum[] = "$bignum"; +const char stringBIGinteger[] = "$integer"; +const char stringBIGbignumstring[] = "$bignum-string"; +const char stringBIGstringbignum[] = "$string-bignum"; +const char stringBIGzerop[] = "$zerop"; +const char stringBIGdecf[] = "$decf"; +const char stringBIGincf[] = "$incf"; +const char stringBIGadd[] = "$+"; +const char stringBIGsub[] = "$-"; +const char stringBIGmul[] = "$*"; +const char stringBIGdiv[] = "$/"; +const char stringBIGmod[] = "$mod"; +const char stringBIGequal[] = "$="; +const char stringBIGless[] = "$<"; +const char stringBIGgreater[] = "$>"; +const char stringBIGlogand[] = "$logand"; +const char stringBIGlogior[] = "$logior"; +const char stringBIGlogxor[] = "$logxor"; +const char stringBIGash[] = "$ash"; + +// Documentation strings +const char docBIGbignum[] = "($bignum int)\n" + "Converts an integer to a bignum and returns it."; +const char docBIGinteger[] = "($integer bignum)\n" + "Converts a bignum to an integer and returns it."; +const char docBIGbignumstring[] = "($bignum-string bignum [base])\n" + "Converts a bignum to a string in base 10 (default) or 16 and returns it."; +const char docBIGstringbignum[] = "($string-bignum bignum [base])\n" + "Converts a bignum to a string in the specified base (default 10) and returns it."; +const char docBIGzerop[] = "($zerop bignum)\n" + "Tests whether a bignum is zero, allowing for trailing zeros."; +const char docBIGadd[] = "($+ bignum1 bignum2)\n" + "Adds two bignums and returns the sum as a new bignum."; +const char docBIGsub[] = "($- bignum1 bignum2)\n" + "Subtracts two bignums and returns the difference as a new bignum."; +const char docBIGmul[] = "($* bignum1 bignum2)\n" + "Multiplies two bignums and returns the product as a new bignum."; +const char docBIGdiv[] = "($/ bignum1 bignum2)\n" + "Divides two bignums and returns the quotient as a new bignum."; +const char docBIGmod[] = "($mod bignum1 bignum2)\n" + "Divides two bignums and returns the remainder as a new bignum."; +const char docBIGequal[] = "($= bignum1 bignum2)\n" + "Returns t if the two bignums are equal."; +const char docBIGless[] = "($< bignum1 bignum2)\n" + "Returns t if bignum1 is less than bignum2."; +const char docBIGgreater[] = "($> bignum1 bignum2)\n" + "Returns t if bignum1 is greater than bignum2."; +const char docBIGlogand[] = "($logand bignum bignum)\n" + "Returns the logical AND of two bignums."; +const char docBIGlogior[] = "($logior bignum bignum)\n" + "Returns the logical inclusive OR of two bignums."; +const char docBIGlogxor[] = "($logxor bignum bignum)\n" + "Returns the logical exclusive OR of two bignums."; +const char docBIGash[] = "($ash bignum shift)\n" + "Returns bignum shifted by shift bits; positive means left."; + +// Symbol lookup table +const tbl_entry_t BignumsTable[] = { + { stringBIGbignum, fn_BIGbignum, MINMAX(FUNCTIONS, 1, 1), docBIGbignum }, + { stringBIGinteger, fn_BIGinteger, MINMAX(FUNCTIONS, 1, 1), docBIGinteger }, + { stringBIGbignumstring, fn_BIGbignumstring, MINMAX(FUNCTIONS, 1, 2), docBIGbignumstring }, + { stringBIGstringbignum, fn_BIGstringbignum, MINMAX(FUNCTIONS, 1, 2), docBIGstringbignum }, + { stringBIGzerop, fn_BIGzerop, MINMAX(FUNCTIONS, 1, 1), docBIGzerop }, + { stringBIGadd, fn_BIGadd, MINMAX(FUNCTIONS, 2, 2), docBIGadd }, + { stringBIGsub, fn_BIGsub, MINMAX(FUNCTIONS, 2, 2), docBIGsub }, + { stringBIGmul, fn_BIGmul, MINMAX(FUNCTIONS, 2, 2), docBIGmul }, + { stringBIGdiv, fn_BIGdiv, MINMAX(FUNCTIONS, 2, 2), docBIGdiv }, + { stringBIGmod, fn_BIGmod, MINMAX(FUNCTIONS, 2, 2), docBIGmod }, + { stringBIGequal, fn_BIGequal, MINMAX(FUNCTIONS, 2, 2), docBIGequal }, + { stringBIGless, fn_BIGless, MINMAX(FUNCTIONS, 2, 2), docBIGless }, + { stringBIGgreater, fn_BIGgreater, MINMAX(FUNCTIONS, 2, 2), docBIGgreater }, + { stringBIGlogand, fn_BIGlogand, MINMAX(FUNCTIONS, 2, 2), docBIGlogand }, + { stringBIGlogior, fn_BIGlogior, MINMAX(FUNCTIONS, 2, 2), docBIGlogior }, + { stringBIGlogxor, fn_BIGlogxor, MINMAX(FUNCTIONS, 2, 2), docBIGlogxor }, + { stringBIGash, fn_BIGash, MINMAX(FUNCTIONS, 2, 2), docBIGash }, +}; diff --git a/extensions.hpp b/extensions.hpp new file mode 100644 index 0000000..fe6d782 --- /dev/null +++ b/extensions.hpp @@ -0,0 +1,146 @@ +#include "esp32-hal-rgb-led.h" +/* + User Extensions +*/ +#include +#include "ulisp.hpp" + +// Definitions +object* fn_now(object* args, object* env) { + (void)env; + static unsigned long Offset; + unsigned long now = millis() / 1000; + int nargs = listlength(args); + + // Set time + if (nargs == 3) { + Offset = (unsigned long)((checkinteger(first(args)) * 60 + checkinteger(second(args))) * 60 + + checkinteger(third(args)) - now); + } else if (nargs > 0) error2(PSTR("wrong number of arguments")); + + // Return time + unsigned long secs = Offset + now; + object* seconds = number(secs % 60); + object* minutes = number((secs / 60) % 60); + object* hours = number((secs / 3600) % 24); + return cons(hours, cons(minutes, cons(seconds, nil))); +} + +const char stringnow[] = "now"; +const char docnow[] = "(now [hh mm ss])\n" + "Sets the current time, or with no arguments returns the current time\n" + "as a list of three integers (hh mm ss)."; + +object* fn_gensym(object* args, object* env) { + unsigned int counter = 0; + char buffer[BUFFERSIZE + 10]; + char prefix[BUFFERSIZE]; + if (args != NULL) { + cstring(checkstring(first(args)), prefix, sizeof(prefix)); + } else { + strcpy(prefix, "$gensym"); + } + object* result; + do { + snprintf(buffer, sizeof(buffer), "%s%u", prefix, counter); + result = buftosymbol(buffer); + counter++; + } while (boundp(result, env)); + return result; +} + +const char stringgensym[] = "gensym"; +const char docgensym[] = "(gensym [prefix])\n" + "Returns a new symbol, optionally beginning with prefix (which must be a string).\n" + "The returned symbol is guaranteed to not conflict with any existing bound symbol."; + +object* fn_intern(object* args, object* env) { + char b[BUFFERSIZE]; + return buftosymbol(cstring(checkstring(first(args)), b, BUFFERSIZE)); +} + +const char stringintern[] = "intern"; +const char docintern[] = "(intern string)\n" + "Creates a symbol, with the same name as the string.\n" + "Unlike gensym, the returned symbol is not modified from the string in any way,\n" + "and so it may be bound."; + +object* fn_sizeof(object* args, object* env) { + int count = 0; + markobject(first(args)); + for (int i = 0; i < WORKSPACESIZE; i++) { + object* obj = &Workspace[i]; + if (marked(obj)) { + unmark(obj); + count++; + } + } + return number(count); +} + +const char stringsizeof[] = "sizeof"; +const char docsizeof[] = "(sizeof obj)\n" + "Returns the number of Lisp cells the object occupies in memory."; + +void destructure(object* structure, object* data, object** env) { + if (structure == nil) return; + if (symbolp(structure)) push(cons(structure, data), *env); + else if (consp(structure)) { + if (!consp(data)) error(canttakecar, data); + destructure(car(structure), car(data), env); + destructure(cdr(structure), cdr(data), env); + } else error(invalidarg, structure); +} + +object* sp_destructuring_bind(object* args, object* env) { + object* structure = first(args); + object* data_expr = second(args); + protect(data_expr); + object* data = eval(data_expr, env); + unprotect(); + object* body = cddr(args); + destructure(structure, data, &env); + protect(body); + object* result = progn_no_tc(body, env); + unprotect(); + return result; +} + +const char stringdestructuringbind[] = "destructuring-bind"; +const char docdestructuringbind[] = "(destructuring-bind structure data [forms*])\n\n" + "Recursively assigns the datums of `data` to the symbols named in `structure`,\n" + "and then evaluates forms in that new environment."; + +object* fn_neopixel(object* args, object* env) { + (void)env; + int r = 0, g = 0, b = 0; + if (listlength(args) == 1) { + int color = checkinteger(first(args)); + if (color > 0xFFFFFF || color < 0) error("color out of range", first(args)); + r = (color >> 16) & 255; + g = (color >> 8) & 255; + b = color & 255; + } else if (listlength(args) == 3) { + r = checkinteger(first(args)); + g = checkinteger(second(args)); + b = checkinteger(third(args)); + if (r > 255) error("red out of range", first(args)); + if (g > 255) error("green out of range", second(args)); + if (b > 255) error("blue out of range", third(args)); + } else error2("don't take 2 args"); + neopixelWrite(2, r, g, b); + return nil; +} + +const char stringneopixel[] = "neopixel"; + +// Symbol lookup table +const tbl_entry_t ExtensionsTable[] = { + { stringnow, fn_now, MINMAX(FUNCTIONS, 0, 3), docnow }, + { stringgensym, fn_gensym, MINMAX(FUNCTIONS, 0, 1), docgensym }, + { stringintern, fn_intern, MINMAX(FUNCTIONS, 1, 1), docintern }, + { stringsizeof, fn_sizeof, MINMAX(FUNCTIONS, 1, 1), docsizeof }, + { stringdestructuringbind, sp_destructuring_bind, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), docdestructuringbind }, + { stringdestructuringbind, sp_destructuring_bind, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), docdestructuringbind }, + { stringneopixel, fn_neopixel, MINMAX(FUNCTIONS, 1, 3), NULL } +}; diff --git a/mine b/mine new file mode 100755 index 0000000..33aef86 --- /dev/null +++ b/mine @@ -0,0 +1,2 @@ +#! /bin/bash +sudo chown --recursive $USER . diff --git a/term.py b/term.py new file mode 100755 index 0000000..76413e2 --- /dev/null +++ b/term.py @@ -0,0 +1,266 @@ +#! /usr/bin/env python3 +from serial import Serial, SerialException +from argparse import ArgumentParser +from prompt_toolkit import Application +from prompt_toolkit.layout import VSplit, HSplit +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.widgets import ( + TextArea, Label, VerticalLine, HorizontalLine) +from prompt_toolkit.layout import Layout +from prompt_toolkit.history import InMemoryHistory +from prompt_toolkit.auto_suggest import AutoSuggestFromHistory +from prompt_toolkit.validation import Validator, ValidationError +from prompt_toolkit.lexers import PygmentsLexer +from prompt_toolkit.formatted_text import HTML +from prompt_toolkit.shortcuts import set_title +from pygments.lexers.lisp import CommonLispLexer +import re +import asyncio + + +input_queue = asyncio.Queue() + + +class LispValidator(Validator): + def validate(self, document): + nesting = 0 + stringmode = False + for c in document.text: + if c == '"': + stringmode = not stringmode + elif c == "(" and not stringmode: + nesting += 1 + elif c == ")" and not stringmode: + nesting -= 1 + if nesting > 0: + raise ValidationError(len(document.text), "Unbalanced parens") + elif stringmode: + raise ValidationError(len(document.text), "Unclosed string") + + +class Watcher: + all_watchers = [] + + def __init__(self, regex): + self.regex = re.compile(regex, re.M) + Watcher.all_watchers.append(self) + + def __call__(self, fun): + self.fun = fun + + def run(self, content: str) -> str: + if m := self.regex.search(content): + foo = self.fun(m) or "" + content = content.replace(m.group(0), foo, 1) + return content + + +def run_watchers(content: str) -> str: + changed = True + while changed: + changed = False + for w in Watcher.all_watchers: + old = content + content = w.run(content) + if content != old: + changed = True + return content + + +WORKSPACESIZE = 1 +FREE = 0 +FREED = 0 +GC_COUNTER = 0 +LAST_ERROR = "" +STATUS = "Loading..." +RIGHT_STATUS = "" + + +@Watcher(r"\{GC#(\d+):(\d+),(\d+)/(\d+)\}") +def mem_usage_watcher(m: re.Match): + global GC_COUNTER + global FREED + global FREE + global WORKSPACESIZE + GC_COUNTER = int(m.group(1)) + FREED = int(m.group(2)) + FREE = int(m.group(3)) + WORKSPACESIZE = int(m.group(4)) + + +@Watcher(r"\[Ready.\]\n") +def ready_watcher(m: re.Match): + global STATUS + if "error" not in STATUS.lower(): + STATUS = "Ready." + + +@Watcher(r"\$!rs=(.*)!\$\n?") +def right_status_watcher(m: re.Match): + global RIGHT_STATUS + RIGHT_STATUS = m.group(1) + + +@Watcher(r"waiting for download") +def bootloader_watcher(m: re.Match): + raise SerialException("Device is in bootloader mode") + + +@Watcher(r"(Error: ([^\n]+))\n") +def error_watcher(m: re.Match): + global STATUS + STATUS = m.group(1) + return m.group(2) + + +@Watcher(r"\a") +def bell_watcher(m: re.Match): + app.output.bell() + + +def memory_usage_bar(): + width = app.output.get_size().columns + usage_percent = 1 - FREE / WORKSPACESIZE + s = f"{FREE}/{WORKSPACESIZE} free ({round(usage_percent * 100, 2)}%) [" + e = f"] (GC #{GC_COUNTER} freed {FREED})" + if usage_percent > 0.75: + color = "#F78" + elif usage_percent > 0.5: + color = "#E90" + else: + color = "#0B3" + bw = width - len(s) - len(e) + nb = round(bw * usage_percent) + bar = "#" * nb + " " * (bw - nb) + return HTML(f"""""") + + +def status_bar(): + width = app.output.get_size().columns + left = STATUS + right = RIGHT_STATUS + spaces = width - len(right) - len(left) + return HTML((left + " " * spaces + right).rstrip("\r\n")) + + +def submit_box(b: Buffer): + input_queue.put_nowait(b.document.text) + return False + + +lispbuffer = TextArea( + multiline=True, + lexer=PygmentsLexer(CommonLispLexer), + focus_on_click=True, + scrollbar=True, + validator=LispValidator(), + history=InMemoryHistory(), + auto_suggest=AutoSuggestFromHistory()) +lispbuffer.allow_scroll_beyond_bottom = True + +terminal = TextArea( + read_only=True, + scrollbar=True) +terminal.allow_scroll_beyond_bottom = True + +command_bar = TextArea( + scrollbar=False, + wrap_lines=False, + lexer=PygmentsLexer(CommonLispLexer), + height=2, + focus_on_click=True, + validator=LispValidator(), + accept_handler=submit_box, + multiline=False, + history=InMemoryHistory(), + auto_suggest=AutoSuggestFromHistory()) + +app = Application(Layout(HSplit([ + VSplit([ + lispbuffer, + VerticalLine(), + terminal + ]), + HorizontalLine(), + VSplit([ + Label(text="cmd> ", dont_extend_width=True, dont_extend_height=True), + command_bar, + ]), + HorizontalLine(), + Label(text=status_bar, dont_extend_height=True), + Label(text=memory_usage_bar, dont_extend_height=True) +])), mouse_support=True, full_screen=True) + + +def output(s: str = ""): + terminal.text += s + terminal.text = terminal.text.replace("\r\n", "\n") + terminal.buffer.cursor_position = len(terminal.text) + + +def startup(port: Serial) -> str: + set_title(f"uLisp on {port.port} ({port.name})") + port.reset_input_buffer() + port.dtr = False + port.dtr = True + output("\n---MCU RESET---\n") + + +async def repl_task(port: Serial): + global STATUS + startup(port) + await asyncio.sleep(0.1) + try: + while True: + # allow other tasks to run + await asyncio.sleep(0.1) + if not input_queue.empty(): + send = await input_queue.get() + match send: + case ".reset": + startup(port) + send = None + case ".quit": + app.exit() + return + case ".run": + send = lispbuffer.text + lispbuffer.buffer.append_to_history() + lispbuffer.text = "" + case _: + pass + if send is not None and send.strip(): + STATUS = "Running..." + port.write(send.encode()) + port.write(b"\r\n") + port.flush() + input_queue.task_done() + if port.in_waiting > 0: + terminal.text += port.read_all().decode() + terminal.text = run_watchers(terminal.text) + output() + except SerialException: + output("Communication error, closing serial port...\n") + port.close() + + +async def main(): + argp = ArgumentParser("term.py") + argp.add_argument("-p", "--port", default="/dev/ttyUSB0") + argp.add_argument("-b", "--baud", default=115200) + foo = argp.parse_args() + port = Serial(foo.port, foo.baud, timeout=0.1, exclusive=True) + + @Watcher(r"uLisp ([\d.a-z]+)") + def version_watcher(m: re.Match): + nonlocal port + set_title(f"uLisp {m.group(1)} on {port.port} ({port.name})") + return f"uLisp version {m.group(1)}" + + await asyncio.gather( + app.run_async(), + repl_task(port)) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/ulisp-esp-comments.ino b/ulisp-esp-comments.ino deleted file mode 100644 index dc63c1f..0000000 --- a/ulisp-esp-comments.ino +++ /dev/null @@ -1,7949 +0,0 @@ -/* uLisp ESP Release 4.6 - www.ulisp.com - David Johnson-Davies - www.technoblogy.com - 13th June 2024 - - Licensed under the MIT license: https://opensource.org/licenses/MIT -*/ - -// Lisp Library -const char LispLibrary[] = ""; - -// Compile options - -// #define resetautorun -#define printfreespace -// #define printgcs -// #define sdcardsupport -// #define gfxsupport -// #define lisplibrary -// #define lineeditor -// #define vt100 -// #define extensions - -// Includes - -// #include "LispLibrary.h" -#include -#include -#include -#include -#include - -#if defined(gfxsupport) -#define COLOR_WHITE ST77XX_WHITE -#define COLOR_BLACK ST77XX_BLACK -#include // Core graphics library -#include // Hardware-specific library for ST7789 -#if defined(ARDUINO_ESP32_DEV) -Adafruit_ST7789 tft = Adafruit_ST7789(5, 16, 19, 18); -#define TFT_BACKLITE 4 -#else -Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, MOSI, SCK, TFT_RST); -#endif -#endif - -#if defined(sdcardsupport) - #include - #define SDSIZE 172 -#else - #define SDSIZE 0 -#endif - -// Platform specific settings - -#define WORDALIGNED __attribute__((aligned (4))) -#define BUFFERSIZE 36 // Number of bits+4 - -#if defined(ARDUINO_FEATHER_ESP32) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) - #define WORKSPACESIZE (8160-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) - #define WORKSPACESIZE (8160-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_FEATHERS2) /* UM FeatherS2 */ - #define WORKSPACESIZE (8160-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32_DEV) /* For TTGO T-Display */ - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32S2_DEV) - #define WORKSPACESIZE (8100-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32C3_DEV) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32S3_DEV) - #define WORKSPACESIZE (22000-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ESP32) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#else -#error "Board not supported!" -#endif - -// C Macros - -#define nil NULL -#define car(x) (((object *) (x))->car) -#define cdr(x) (((object *) (x))->cdr) - -#define first(x) car(x) -#define rest(x) cdr(x) -#define second(x) first(rest(x)) -#define cddr(x) cdr(cdr(x)) -#define third(x) first(cddr(x)) - -#define push(x, y) ((y) = cons((x),(y))) -#define pop(y) ((y) = cdr(y)) - -#define protect(y) push((y), GCStack) -#define unprotect() pop(GCStack) - -#define integerp(x) ((x) != NULL && (x)->type == NUMBER) -#define floatp(x) ((x) != NULL && (x)->type == FLOAT) -#define symbolp(x) ((x) != NULL && (x)->type == SYMBOL) -#define stringp(x) ((x) != NULL && (x)->type == STRING) -#define characterp(x) ((x) != NULL && (x)->type == CHARACTER) -#define arrayp(x) ((x) != NULL && (x)->type == ARRAY) -#define streamp(x) ((x) != NULL && (x)->type == STREAM) - -#define mark(x) (car(x) = (object *)(((uintptr_t)(car(x))) | MARKBIT)) -#define unmark(x) (car(x) = (object *)(((uintptr_t)(car(x))) & ~MARKBIT)) -#define marked(x) ((((uintptr_t)(car(x))) & MARKBIT) != 0) -#define MARKBIT 1 - -#define setflag(x) (Flags |= 1<<(x)) -#define clrflag(x) (Flags &= ~(1<<(x))) -#define tstflag(x) (Flags & 1<<(x)) - -#define issp(x) (x == ' ' || x == '\n' || x == '\r' || x == '\t') -#define isbr(x) (x == ')' || x == '(' || x == '"' || x == '#') -#define longsymbolp(x) (((x)->name & 0x03) == 0) -#define longnamep(x) (((x) & 0x03) == 0) -#define arraysize(x) (sizeof(x) / sizeof(x[0])) -#define stringifyX(x) #x -#define stringify(x) stringifyX(x) -#define PACKEDS 0x43238000 -#define BUILTINS 0xF4240000 -#define ENDFUNCTIONS 1536 - -// Constants - -const int TRACEMAX = 3; // Number of traced functions -enum type { ZZERO=0, SYMBOL=2, CODE=4, NUMBER=6, STREAM=8, CHARACTER=10, FLOAT=12, ARRAY=14, STRING=16, PAIR=18 }; // ARRAY STRING and PAIR must be last -enum token { UNUSED, BRA, KET, QUO, DOT }; -enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM, WIFISTREAM, STRINGSTREAM, GFXSTREAM }; -enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS }; - -// Stream names used by printobject -const char serialstream[] = "serial"; -const char i2cstream[] = "i2c"; -const char spistream[] = "spi"; -const char sdstream[] = "sd"; -const char wifistream[] = "wifi"; -const char stringstream[] = "string"; -const char gfxstream[] = "gfx"; -const char *const streamname[] = {serialstream, i2cstream, spistream, sdstream, wifistream, stringstream, gfxstream}; - -// Typedefs - -typedef uint32_t symbol_t; -typedef uint32_t builtin_t; -typedef uint32_t chars_t; - -typedef struct sobject { - union { - struct { - sobject *car; - sobject *cdr; - }; - struct { - unsigned int type; - union { - symbol_t name; - int integer; - chars_t chars; // For strings - float single_float; - }; - }; - }; -} object; - -typedef object *(*fn_ptr_type)(object *, object *); -typedef void (*mapfun_t)(object *, object **); - -typedef const struct { - const char *string; - fn_ptr_type fptr; - uint8_t minmax; - const char *doc; -} tbl_entry_t; - -typedef int (*gfun_t)(); -typedef void (*pfun_t)(char); - -enum builtins: builtin_t { NIL, TEE, NOTHING, OPTIONAL, FEATURES, INITIALELEMENT, ELEMENTTYPE, TEST, BIT, AMPREST, -LAMBDA, LET, LETSTAR, CLOSURE, PSTAR, QUOTE, DEFUN, DEFVAR, EQ, CAR, FIRST, CDR, REST, NTH, AREF, CHAR, -STRINGFN, PINMODE, DIGITALWRITE, ANALOGREAD, REGISTER, FORMAT, - }; - -// Global variables - -object Workspace[WORKSPACESIZE] WORDALIGNED; - -jmp_buf toplevel_handler; -jmp_buf *handler = &toplevel_handler; -unsigned int Freespace = 0; -object *Freelist; -unsigned int I2Ccount; -unsigned int TraceFn[TRACEMAX]; -unsigned int TraceDepth[TRACEMAX]; -builtin_t Context; - -object *GlobalEnv; -object *GCStack = NULL; -object *GlobalString; -object *GlobalStringTail; -int GlobalStringIndex = 0; -uint8_t PrintCount = 0; -uint8_t BreakLevel = 0; -char LastChar = 0; -char LastPrint = 0; - -// Flags -enum flag { PRINTREADABLY, RETURNFLAG, ESCAPE, EXITEDITOR, LIBRARYLOADED, NOESC, NOECHO, MUFFLEERRORS }; -volatile uint8_t Flags = 0b00001; // PRINTREADABLY set by default - -// Forward references -object *tee; -void pfstring (const char *s, pfun_t pfun); - -inline symbol_t twist (builtin_t x) { - return (x<<2) | ((x & 0xC0000000)>>30); -} - -inline builtin_t untwist (symbol_t x) { - return (x>>2 & 0x3FFFFFFF) | ((x & 0x03)<<30); -} - -// Error handling - -/* - errorsub - used by all the error routines. - Prints: "Error: 'fname' string", where fname is the name of the Lisp function in which the error occurred. -*/ -void errorsub (symbol_t fname, const char *string) { - pfl(pserial); pfstring("Error: ", pserial); - if (fname != sym(NIL)) { - pserial('\''); - psymbol(fname, pserial); - pserial('\''); pserial(' '); - } - pfstring(string, pserial); -} - -void errorend () { GCStack = NULL; longjmp(*handler, 1); } - -/* - errorsym - prints an error message and reenters the REPL. - Prints: "Error: 'fname' string: symbol", where fname is the name of the user Lisp function in which the error occurred, - and symbol is the object generating the error. -*/ -void errorsym (symbol_t fname, const char *string, object *symbol) { - if (!tstflag(MUFFLEERRORS)) { - errorsub(fname, string); - pserial(':'); pserial(' '); - printobject(symbol, pserial); - pln(pserial); - } - errorend(); -} - -/* - errorsym2 - prints an error message and reenters the REPL. - Prints: "Error: 'fname' string", where fname is the name of the user Lisp function in which the error occurred. -*/ -void errorsym2 (symbol_t fname, const char *string) { - if (!tstflag(MUFFLEERRORS)) { - errorsub(fname, string); - pln(pserial); - } - errorend(); -} - -/* - error - prints an error message and reenters the REPL. - Prints: "Error: 'Context' string: symbol", where Context is the name of the built-in Lisp function in which the error occurred, - and symbol is the object generating the error. -*/ -void error (const char *string, object *symbol) { - errorsym(sym(Context), string, symbol); -} - -/* - error2 - prints an error message and reenters the REPL. - Prints: "Error: 'Context' string", where Context is the name of the built-in Lisp function in which the error occurred. -*/ -void error2 (const char *string) { - errorsym2(sym(Context), string); -} - -/* - formaterr - displays a format error with a ^ pointing to the error -*/ -void formaterr (object *formatstr, const char *string, uint8_t p) { - pln(pserial); indent(4, ' ', pserial); printstring(formatstr, pserial); pln(pserial); - indent(p+5, ' ', pserial); pserial('^'); - error2(string); - pln(pserial); - GCStack = NULL; - longjmp(*handler, 1); -} - -// Save space as these are used multiple times -const char notanumber[] = "argument is not a number"; -const char notaninteger[] = "argument is not an integer"; -const char notastring[] = "argument is not a string"; -const char notalist[] = "argument is not a list"; -const char notasymbol[] = "argument is not a symbol"; -const char notproper[] = "argument is not a proper list"; -const char toomanyargs[] = "too many arguments"; -const char toofewargs[] = "too few arguments"; -const char noargument[] = "missing argument"; -const char nostream[] = "missing stream argument"; -const char overflow[] = "arithmetic overflow"; -const char divisionbyzero[] = "division by zero"; -const char indexnegative[] = "index can't be negative"; -const char invalidarg[] = "invalid argument"; -const char invalidkey[] = "invalid keyword"; -const char illegalclause[] = "illegal clause"; -const char invalidpin[] = "invalid pin"; -const char oddargs[] = "odd number of arguments"; -const char indexrange[] = "index out of range"; -const char canttakecar[] = "can't take car"; -const char canttakecdr[] = "can't take cdr"; -const char unknownstreamtype[] = "unknown stream type"; - -// Set up workspace - -/* - initworkspace - initialises the workspace into a linked list of free objects -*/ -void initworkspace () { - Freelist = NULL; - for (int i=WORKSPACESIZE-1; i>=0; i--) { - object *obj = &Workspace[i]; - car(obj) = NULL; - cdr(obj) = Freelist; - Freelist = obj; - Freespace++; - } -} - -/* - myalloc - returns the first object from the linked list of free objects -*/ -object *myalloc () { - if (Freespace == 0) { Context = NIL; error2("no room"); } - object *temp = Freelist; - Freelist = cdr(Freelist); - Freespace--; - return temp; -} - -/* - myfree - adds obj to the linked list of free objects. - inline makes gc significantly faster -*/ -inline void myfree (object *obj) { - car(obj) = NULL; - cdr(obj) = Freelist; - Freelist = obj; - Freespace++; -} - -// Make each type of object - -/* - number - make an integer object with value n and return it -*/ -object *number (int n) { - object *ptr = myalloc(); - ptr->type = NUMBER; - ptr->integer = n; - return ptr; -} - -/* - makefloat - make a floating point object with value f and return it -*/ -object *makefloat (float f) { - object *ptr = myalloc(); - ptr->type = FLOAT; - ptr->single_float = f; - return ptr; -} - -/* - character - make a character object with value c and return it -*/ -object *character (uint8_t c) { - object *ptr = myalloc(); - ptr->type = CHARACTER; - ptr->chars = c; - return ptr; -} - -/* - cons - make a cons with arg1 and arg2 return it -*/ -object *cons (object *arg1, object *arg2) { - object *ptr = myalloc(); - ptr->car = arg1; - ptr->cdr = arg2; - return ptr; -} - -/* - symbol - make a symbol object with value name and return it -*/ -object *symbol (symbol_t name) { - object *ptr = myalloc(); - ptr->type = SYMBOL; - ptr->name = name; - return ptr; -} - -/* - bsymbol - make a built-in symbol -*/ -inline object *bsymbol (builtin_t name) { - return intern(twist(name+BUILTINS)); -} - -/* - intern - looks through the workspace for an existing occurrence of symbol name and returns it, - otherwise calls symbol(name) to create a new symbol. -*/ -object *intern (symbol_t name) { - for (int i=0; itype == SYMBOL && obj->name == name) return obj; - } - return symbol(name); -} - -/* - eqsymbols - compares the long string/symbol obj with the string in buffer. -*/ -bool eqsymbols (object *obj, char *buffer) { - object *arg = cdr(obj); - int i = 0; - while (!(arg == NULL && buffer[i] == 0)) { - if (arg == NULL || buffer[i] == 0) return false; - int test = 0, shift = 24; - for (int j=0; j<4; j++, i++) { - if (buffer[i] == 0) break; - test = test | buffer[i]<chars != test) return false; - arg = car(arg); - } - return true; -} - -/* - internlong - looks through the workspace for an existing occurrence of the long symbol in buffer and returns it, - otherwise calls lispstring(buffer) to create a new symbol. -*/ -object *internlong (char *buffer) { - for (int i=0; itype == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) return obj; - } - object *obj = lispstring(buffer); - obj->type = SYMBOL; - return obj; -} - -/* - stream - makes a stream object defined by streamtype and address, and returns it -*/ -object *stream (uint8_t streamtype, uint8_t address) { - object *ptr = myalloc(); - ptr->type = STREAM; - ptr->integer = streamtype<<8 | address; - return ptr; -} - -/* - newstring - makes an empty string object and returns it -*/ -object *newstring () { - object *ptr = myalloc(); - ptr->type = STRING; - ptr->chars = 0; - return ptr; -} - -// Features - -const char floatingpoint[] = ":floating-point"; -const char arrays[] = ":arrays"; -const char doc[] = ":documentation"; -const char errorhandling[] = ":error-handling"; -const char wifi[] = ":wi-fi"; -const char gfx[] = ":gfx"; - -/* - features - create a list of features symbols from const strings. -*/ -object *features () { - object *result = NULL; - push(internlong((char *)gfx), result); - push(internlong((char *)wifi), result); - push(internlong((char *)errorhandling), result); - push(internlong((char *)doc), result); - push(internlong((char *)arrays), result); - push(internlong((char *)floatingpoint), result); - return result; -} - -// Garbage collection - -/* - markobject - recursively marks reachable objects, starting from obj -*/ -void markobject (object *obj) { - MARK: - if (obj == NULL) return; - if (marked(obj)) return; - - object* arg = car(obj); - unsigned int type = obj->type; - mark(obj); - - if (type >= PAIR || type == ZZERO) { // cons - markobject(arg); - obj = cdr(obj); - goto MARK; - } - - if (type == ARRAY) { - obj = cdr(obj); - goto MARK; - } - - if ((type == STRING) || (type == SYMBOL && longsymbolp(obj))) { - obj = cdr(obj); - while (obj != NULL) { - arg = car(obj); - mark(obj); - obj = arg; - } - } -} - -/* - sweep - goes through the workspace freeing objects that have not been marked, - and unmarks marked objects -*/ -void sweep () { - Freelist = NULL; - Freespace = 0; - for (int i=WORKSPACESIZE-1; i>=0; i--) { - object *obj = &Workspace[i]; - if (!marked(obj)) myfree(obj); else unmark(obj); - } -} - -/* - gc - performs garbage collection by calling markobject() on each of the pointers to objects in use, - followed by sweep() to free unused objects. -*/ -void gc (object *form, object *env) { - #if defined(printgcs) - int start = Freespace; - #endif - markobject(tee); - markobject(GlobalEnv); - markobject(GCStack); - markobject(form); - markobject(env); - sweep(); - #if defined(printgcs) - pfl(pserial); pserial('{'); pint(Freespace - start, pserial); pserial('}'); - #endif -} - -// Compact image - -/* - movepointer - corrects pointers to an object that has moved from 'from' to 'to' -*/ -void movepointer (object *from, object *to) { - for (int i=0; itype) & ~MARKBIT; - if (marked(obj) && (type >= ARRAY || type==ZZERO || (type == SYMBOL && longsymbolp(obj)))) { - if (car(obj) == (object *)((uintptr_t)from | MARKBIT)) - car(obj) = (object *)((uintptr_t)to | MARKBIT); - if (cdr(obj) == from) cdr(obj) = to; - } - } - // Fix strings and long symbols - for (int i=0; itype) & ~MARKBIT; - if (type == STRING || (type == SYMBOL && longsymbolp(obj))) { - obj = cdr(obj); - while (obj != NULL) { - if (cdr(obj) == to) cdr(obj) = from; - obj = (object *)((uintptr_t)(car(obj)) & ~MARKBIT); - } - } - } - } -} - -/* - compactimage - compacts the image by moving objects to the lowest possible position in the workspace -*/ -uintptr_t compactimage (object **arg) { - markobject(tee); - markobject(GlobalEnv); - markobject(GCStack); - object *firstfree = Workspace; - while (marked(firstfree)) firstfree++; - object *obj = &Workspace[WORKSPACESIZE-1]; - while (firstfree < obj) { - if (marked(obj)) { - car(firstfree) = car(obj); - cdr(firstfree) = cdr(obj); - unmark(obj); - movepointer(obj, firstfree); - if (GlobalEnv == obj) GlobalEnv = firstfree; - if (GCStack == obj) GCStack = firstfree; - if (*arg == obj) *arg = firstfree; - while (marked(firstfree)) firstfree++; - } - obj--; - } - sweep(); - return firstfree - Workspace; -} - -// Make SD card filename - -char *MakeFilename (object *arg, char *buffer) { - int max = BUFFERSIZE-1; - buffer[0]='/'; - int i = 1; - do { - char c = nthchar(arg, i-1); - if (c == '\0') break; - buffer[i++] = c; - } while (i>8 & 0xFF); - file.write(data>>16 & 0xFF); file.write(data>>24 & 0xFF); -} - -int SDReadInt (File file) { - uintptr_t b0 = file.read(); uintptr_t b1 = file.read(); - uintptr_t b2 = file.read(); uintptr_t b3 = file.read(); - return b0 | b1<<8 | b2<<16 | b3<<24; -} -#elif defined(LITTLEFS) -void FSWrite32 (File file, uint32_t data) { - union { uint32_t data2; uint8_t u8[4]; }; - data2 = data; - if (file.write(u8, 4) != 4) error2("not enough room"); -} - -uint32_t FSRead32 (File file) { - union { uint32_t data; uint8_t u8[4]; }; - file.read(u8, 4); - return data; -} -#else -void EpromWriteInt(int *addr, uintptr_t data) { - EEPROM.write((*addr)++, data & 0xFF); EEPROM.write((*addr)++, data>>8 & 0xFF); - EEPROM.write((*addr)++, data>>16 & 0xFF); EEPROM.write((*addr)++, data>>24 & 0xFF); -} - -int EpromReadInt (int *addr) { - uint8_t b0 = EEPROM.read((*addr)++); uint8_t b1 = EEPROM.read((*addr)++); - uint8_t b2 = EEPROM.read((*addr)++); uint8_t b3 = EEPROM.read((*addr)++); - return b0 | b1<<8 | b2<<16 | b3<<24; -} -#endif - -unsigned int saveimage (object *arg) { -#if defined(sdcardsupport) - unsigned int imagesize = compactimage(&arg); - SD.begin(SDCARD_SS_PIN); - File file; - if (stringp(arg)) { - char buffer[BUFFERSIZE]; - file = SD.open(MakeFilename(arg, buffer), FILE_WRITE); - if (!file) error2("problem saving to SD card or invalid filename"); - arg = NULL; - } else if (arg == NULL || listp(arg)) { - file = SD.open("/ULISP.IMG", FILE_WRITE); - if (!file) error2("problem saving to SD card"); - } else error(invalidarg, arg); - SDWriteInt(file, (uintptr_t)arg); - SDWriteInt(file, imagesize); - SDWriteInt(file, (uintptr_t)GlobalEnv); - SDWriteInt(file, (uintptr_t)GCStack); - for (unsigned int i=0; i EEPROMSIZE) error("image too large", number(imagesize)); - EEPROM.begin(EEPROMSIZE); - int addr = 0; - EpromWriteInt(&addr, (uintptr_t)arg); - EpromWriteInt(&addr, imagesize); - EpromWriteInt(&addr, (uintptr_t)GlobalEnv); - EpromWriteInt(&addr, (uintptr_t)GCStack); - for (unsigned int i=0; itype; - return type >= PAIR || type == ZZERO; -} - -/* - atom - implements Lisp atom -*/ -#define atom(x) (!consp(x)) - -/* - listp - implements Lisp listp -*/ -bool listp (object *x) { - if (x == NULL) return true; - unsigned int type = x->type; - return type >= PAIR || type == ZZERO; -} - -/* - improperp - tests whether x is an improper list -*/ -#define improperp(x) (!listp(x)) - -object *quote (object *arg) { - return cons(bsymbol(QUOTE), cons(arg,NULL)); -} - -// Radix 40 encoding - -/* - builtin - converts a symbol name to builtin -*/ -builtin_t builtin (symbol_t name) { - return (builtin_t)(untwist(name) - BUILTINS); -} - -/* - sym - converts a builtin to a symbol name -*/ -symbol_t sym (builtin_t x) { - return twist(x + BUILTINS); -} - -/* - toradix40 - returns a number from 0 to 39 if the character can be encoded, or -1 otherwise. -*/ -int8_t toradix40 (char ch) { - if (ch == 0) return 0; - if (ch >= '0' && ch <= '9') return ch-'0'+1; - if (ch == '-') return 37; if (ch == '*') return 38; if (ch == '$') return 39; - ch = ch | 0x20; - if (ch >= 'a' && ch <= 'z') return ch-'a'+11; - return -1; // Invalid -} - -/* - fromradix40 - returns the character encoded by the number n. -*/ -char fromradix40 (char n) { - if (n >= 1 && n <= 10) return '0'+n-1; - if (n >= 11 && n <= 36) return 'a'+n-11; - if (n == 37) return '-'; if (n == 38) return '*'; if (n == 39) return '$'; - return 0; -} - -/* - pack40 - packs six radix40-encoded characters from buffer into a 32-bit number and returns it. -*/ -uint32_t pack40 (char *buffer) { - int x = 0, j = 0; - for (int i=0; i<6; i++) { - x = x * 40 + toradix40(buffer[j]); - if (buffer[j] != 0) j++; - } - return x; -} - -/* - valid40 - returns true if the symbol in buffer can be encoded as six radix40-encoded characters. -*/ -bool valid40 (char *buffer) { - int t = 11; - for (int i=0; i<6; i++) { - if (toradix40(buffer[i]) < t) return false; - if (buffer[i] == 0) break; - t = 0; - } - return true; -} - -/* - digitvalue - returns the numerical value of a hexadecimal digit, or 16 if invalid. -*/ -int8_t digitvalue (char d) { - if (d>='0' && d<='9') return d-'0'; - d = d | 0x20; - if (d>='a' && d<='f') return d-'a'+10; - return 16; -} - -/* - checkinteger - check that obj is an integer and return it -*/ -int checkinteger (object *obj) { - if (!integerp(obj)) error(notaninteger, obj); - return obj->integer; -} - -/* - checkbitvalue - check that obj is an integer equal to 0 or 1 and return it -*/ -int checkbitvalue (object *obj) { - if (!integerp(obj)) error(notaninteger, obj); - int n = obj->integer; - if (n & ~1) error("argument is not a bit value", obj); - return n; -} - -/* - checkintfloat - check that obj is an integer or floating-point number and return the number -*/ -float checkintfloat (object *obj) { - if (integerp(obj)) return (float)obj->integer; - if (!floatp(obj)) error(notanumber, obj); - return obj->single_float; -} - -/* - checkchar - check that obj is a character and return the character -*/ -int checkchar (object *obj) { - if (!characterp(obj)) error("argument is not a character", obj); - return obj->chars; -} - -/* - checkstring - check that obj is a string -*/ -object *checkstring (object *obj) { - if (!stringp(obj)) error(notastring, obj); - return obj; -} - -int isstream (object *obj){ - if (!streamp(obj)) error("not a stream", obj); - return obj->integer; -} - -int isbuiltin (object *obj, builtin_t n) { - return symbolp(obj) && obj->name == sym(n); -} - -bool builtinp (symbol_t name) { - return (untwist(name) >= BUILTINS); -} - -int checkkeyword (object *obj) { - if (!keywordp(obj)) error("argument is not a keyword", obj); - builtin_t kname = builtin(obj->name); - uint8_t context = getminmax(kname); - if (context != 0 && context != Context) error(invalidkey, obj); - return ((int)lookupfn(kname)); -} - -/* - checkargs - checks that the number of objects in the list args - is within the range specified in the symbol lookup table -*/ -void checkargs (object *args) { - int nargs = listlength(args); - checkminmax(Context, nargs); -} - -/* - eq - implements Lisp eq -*/ -boolean eq (object *arg1, object *arg2) { - if (arg1 == arg2) return true; // Same object - if ((arg1 == nil) || (arg2 == nil)) return false; // Not both values - if (arg1->cdr != arg2->cdr) return false; // Different values - if (symbolp(arg1) && symbolp(arg2)) return true; // Same symbol - if (integerp(arg1) && integerp(arg2)) return true; // Same integer - if (floatp(arg1) && floatp(arg2)) return true; // Same float - if (characterp(arg1) && characterp(arg2)) return true; // Same character - return false; -} - -/* - equal - implements Lisp equal -*/ -bool equal (object *arg1, object *arg2) { - if (stringp(arg1) && stringp(arg2)) return (stringcompare(cons(arg1, cons(arg2, nil)), false, false, true) != -1); - if (consp(arg1) && consp(arg2)) return (equal(car(arg1), car(arg2)) && equal(cdr(arg1), cdr(arg2))); - return eq(arg1, arg2); -} - -/* - listlength - returns the length of a list -*/ -int listlength (object *list) { - int length = 0; - while (list != NULL) { - if (improperp(list)) error2(notproper); - list = cdr(list); - length++; - } - return length; -} - -/* - checkarguments - checks the arguments list in a special form such as with-xxx, - dolist, or dotimes. -*/ -object *checkarguments (object *args, int min, int max) { - if (args == NULL) error2(noargument); - args = first(args); - if (!listp(args)) error(notalist, args); - int length = listlength(args); - if (length < min) error(toofewargs, args); - if (length > max) error(toomanyargs, args); - return args; -} - -// Mathematical helper functions - -/* - add_floats - used by fn_add - Converts the numbers in args to floats, adds them to fresult, and returns the sum as a Lisp float. -*/ -object *add_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - fresult = fresult + checkintfloat(arg); - args = cdr(args); - } - return makefloat(fresult); -} - -/* - subtract_floats - used by fn_subtract with more than one argument - Converts the numbers in args to floats, subtracts them from fresult, and returns the result as a Lisp float. -*/ -object *subtract_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - fresult = fresult - checkintfloat(arg); - args = cdr(args); - } - return makefloat(fresult); -} - -/* - negate - used by fn_subtract with one argument - If the result is an integer, and negating it doesn't overflow, keep the result as an integer. - Otherwise convert the result to a float, negate it, and return the result as a Lisp float. -*/ -object *negate (object *arg) { - if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MIN) return makefloat(-result); - else return number(-result); - } else if (floatp(arg)) return makefloat(-(arg->single_float)); - else error(notanumber, arg); - return nil; -} - -/* - multiply_floats - used by fn_multiply - Converts the numbers in args to floats, adds them to fresult, and returns the result as a Lisp float. -*/ -object *multiply_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - fresult = fresult * checkintfloat(arg); - args = cdr(args); - } - return makefloat(fresult); -} - -/* - divide_floats - used by fn_divide - Converts the numbers in args to floats, divides fresult by them, and returns the result as a Lisp float. -*/ -object *divide_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - float f = checkintfloat(arg); - if (f == 0.0) error2(divisionbyzero); - fresult = fresult / f; - args = cdr(args); - } - return makefloat(fresult); -} - -/* - compare - a generic compare function - Used to implement the other comparison functions. - If lt is true the result is true if each argument is less than the next argument. - If gt is true the result is true if each argument is greater than the next argument. - If eq is true the result is true if each argument is equal to the next argument. -*/ -object *compare (object *args, bool lt, bool gt, bool eq) { - object *arg1 = first(args); - args = cdr(args); - while (args != NULL) { - object *arg2 = first(args); - if (integerp(arg1) && integerp(arg2)) { - if (!lt && ((arg1->integer) < (arg2->integer))) return nil; - if (!eq && ((arg1->integer) == (arg2->integer))) return nil; - if (!gt && ((arg1->integer) > (arg2->integer))) return nil; - } else { - if (!lt && (checkintfloat(arg1) < checkintfloat(arg2))) return nil; - if (!eq && (checkintfloat(arg1) == checkintfloat(arg2))) return nil; - if (!gt && (checkintfloat(arg1) > checkintfloat(arg2))) return nil; - } - arg1 = arg2; - args = cdr(args); - } - return tee; -} - -/* - intpower - calculates base to the power exp as an integer -*/ -int intpower (int base, int exp) { - int result = 1; - while (exp) { - if (exp & 1) result = result * base; - exp = exp / 2; - base = base * base; - } - return result; -} - -// Association lists - -/* - testargument - handles the :test argument for functions that accept it -*/ -object *testargument (object *args) { - object *test = bsymbol(EQ); - if (args != NULL) { - if (cdr(args) == NULL) error2("unpaired keyword"); - if ((isbuiltin(first(args), TEST))) test = second(args); - else error("unsupported keyword", first(args)); - } - return test; -} - -/* - delassoc - deletes the pair matching key from an association list and returns the key, or nil if not found -*/ -object *delassoc (object *key, object **alist) { - object *list = *alist; - object *prev = NULL; - while (list != NULL) { - object *pair = first(list); - if (eq(key,car(pair))) { - if (prev == NULL) *alist = cdr(list); - else cdr(prev) = cdr(list); - return key; - } - prev = list; - list = cdr(list); - } - return nil; -} - -// Array utilities - -/* - nextpower2 - returns the smallest power of 2 that is equal to or greater than n -*/ -int nextpower2 (int n) { - n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; - n |= n >> 8; n |= n >> 16; n++; - return n<2 ? 2 : n; -} - -/* - buildarray - builds an array with n elements using a tree of size s which must be a power of 2 - The elements are initialised to the default def -*/ -object *buildarray (int n, int s, object *def) { - int s2 = s>>1; - if (s2 == 1) { - if (n == 2) return cons(def, def); - else if (n == 1) return cons(def, NULL); - else return NULL; - } else if (n >= s2) return cons(buildarray(s2, s2, def), buildarray(n - s2, s2, def)); - else return cons(buildarray(n, s2, def), nil); -} - -object *makearray (object *dims, object *def, bool bitp) { - int size = 1; - object *dimensions = dims; - while (dims != NULL) { - int d = car(dims)->integer; - if (d < 0) error2("dimension can't be negative"); - size = size * d; - dims = cdr(dims); - } - // Bit array identified by making first dimension negative - if (bitp) { - size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - car(dimensions) = number(-(car(dimensions)->integer)); - } - object *ptr = myalloc(); - ptr->type = ARRAY; - object *tree = nil; - if (size != 0) tree = buildarray(size, nextpower2(size), def); - ptr->cdr = cons(tree, dimensions); - return ptr; -} - -/* - arrayref - returns a pointer to the element specified by index in the array of size s -*/ -object **arrayref (object *array, int index, int size) { - int mask = nextpower2(size)>>1; - object **p = &car(cdr(array)); - while (mask) { - if ((index & mask) == 0) p = &(car(*p)); else p = &(cdr(*p)); - mask = mask>>1; - } - return p; -} - -/* - getarray - gets a pointer to an element in a multi-dimensional array, given a list of the subscripts subs - If the first subscript is negative it's a bit array and bit is set to the bit number -*/ -object **getarray (object *array, object *subs, object *env, int *bit) { - int index = 0, size = 1, s; - *bit = -1; - bool bitp = false; - object *dims = cddr(array); - while (dims != NULL && subs != NULL) { - int d = car(dims)->integer; - if (d < 0) { d = -d; bitp = true; } - if (env) s = checkinteger(eval(car(subs), env)); else s = checkinteger(car(subs)); - if (s < 0 || s >= d) error("subscript out of range", car(subs)); - size = size * d; - index = index * d + s; - dims = cdr(dims); subs = cdr(subs); - } - if (dims != NULL) error2("too few subscripts"); - if (subs != NULL) error2("too many subscripts"); - if (bitp) { - size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - *bit = index & (sizeof(int)==4 ? 0x1F : 0x0F); - index = index>>(sizeof(int)==4 ? 5 : 4); - } - return arrayref(array, index, size); -} - -/* - rslice - reads a slice of an array recursively -*/ -void rslice (object *array, int size, int slice, object *dims, object *args) { - int d = first(dims)->integer; - for (int i = 0; i < d; i++) { - int index = slice * d + i; - if (!consp(args)) error2("initial contents don't match array type"); - if (cdr(dims) == NULL) { - object **p = arrayref(array, index, size); - *p = car(args); - } else rslice(array, size, index, cdr(dims), car(args)); - args = cdr(args); - } -} - -/* - readarray - reads a list structure from args and converts it to a d-dimensional array. - Uses rslice for each of the slices of the array. -*/ -object *readarray (int d, object *args) { - object *list = args; - object *dims = NULL; object *head = NULL; - int size = 1; - for (int i = 0; i < d; i++) { - if (!listp(list)) error2("initial contents don't match array type"); - int l = listlength(list); - if (dims == NULL) { dims = cons(number(l), NULL); head = dims; } - else { cdr(dims) = cons(number(l), NULL); dims = cdr(dims); } - size = size * l; - if (list != NULL) list = car(list); - } - object *array = makearray(head, NULL, false); - rslice(array, size, 0, head, args); - return array; -} - -/* - readbitarray - reads an item in the format #*1010101000110 by reading it and returning a list of integers, - and then converting that to a bit array -*/ -object *readbitarray (gfun_t gfun) { - char ch = gfun(); - object *head = NULL; - object *tail = NULL; - while (!issp(ch) && !isbr(ch)) { - if (ch != '0' && ch != '1') error2("illegal character in bit array"); - object *cell = cons(number(ch - '0'), NULL); - if (head == NULL) head = cell; - else tail->cdr = cell; - tail = cell; - ch = gfun(); - } - LastChar = ch; - int size = listlength(head); - object *array = makearray(cons(number(size), NULL), number(0), true); - size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - int index = 0; - while (head != NULL) { - object **loc = arrayref(array, index>>(sizeof(int)==4 ? 5 : 4), size); - int bit = index & (sizeof(int)==4 ? 0x1F : 0x0F); - *loc = number((((*loc)->integer) & ~(1<integer)<integer; - if (d < 0) d = -d; - for (int i = 0; i < d; i++) { - if (i && spaces) pfun(' '); - int index = slice * d + i; - if (cdr(dims) == NULL) { - if (bitp) pint(((*arrayref(array, index>>(sizeof(int)==4 ? 5 : 4), size))->integer)>> - (index & (sizeof(int)==4 ? 0x1F : 0x0F)) & 1, pfun); - else printobject(*arrayref(array, index, size), pfun); - } else { pfun('('); pslice(array, size, index, cdr(dims), pfun, bitp); pfun(')'); } - } -} - -/* - printarray - prints an array in the appropriate Lisp format -*/ -void printarray (object *array, pfun_t pfun) { - object *dimensions = cddr(array); - object *dims = dimensions; - bool bitp = false; - int size = 1, n = 0; - while (dims != NULL) { - int d = car(dims)->integer; - if (d < 0) { bitp = true; d = -d; } - size = size * d; - dims = cdr(dims); n++; - } - if (bitp) size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - pfun('#'); - if (n == 1 && bitp) { pfun('*'); pslice(array, size, -1, dimensions, pfun, bitp); } - else { - if (n > 1) { pint(n, pfun); pfun('A'); } - pfun('('); pslice(array, size, 0, dimensions, pfun, bitp); pfun(')'); - } -} - -// String utilities - -void indent (uint8_t spaces, char ch, pfun_t pfun) { - for (uint8_t i=0; ichars & 0xFFFFFF) == 0) { - (*tail)->chars |= ch<<16; return; - } else if (((*tail)->chars & 0xFFFF) == 0) { - (*tail)->chars |= ch<<8; return; - } else if (((*tail)->chars & 0xFF) == 0) { - (*tail)->chars |= ch; return; - } else { - cell = myalloc(); car(*tail) = cell; - } - car(cell) = NULL; cell->chars = ch<<24; *tail = cell; -} - -/* - copystring - returns a copy of a Lisp string -*/ -object *copystring (object *arg) { - object *obj = newstring(); - object *ptr = obj; - arg = cdr(arg); - while (arg != NULL) { - object *cell = myalloc(); car(cell) = NULL; - if (cdr(obj) == NULL) cdr(obj) = cell; else car(ptr) = cell; - ptr = cell; - ptr->chars = arg->chars; - arg = car(arg); - } - return obj; -} - -/* - readstring - reads characters from an input stream up to delimiter delim - and returns a Lisp string -*/ -object *readstring (uint8_t delim, bool esc, gfun_t gfun) { - object *obj = newstring(); - object *tail = obj; - int ch = gfun(); - if (ch == -1) return nil; - while ((ch != delim) && (ch != -1)) { - if (esc && ch == '\\') ch = gfun(); - buildstring(ch, &tail); - ch = gfun(); - } - return obj; -} - -/* - stringlength - returns the length of a Lisp string - Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word -*/ -int stringlength (object *form) { - int length = 0; - form = cdr(form); - while (form != NULL) { - int chars = form->chars; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - if (chars>>i & 0xFF) length++; - } - form = car(form); - } - return length; -} - -/* - getcharplace - gets character n in a Lisp string, and sets shift to (- the shift position -2) - Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word. -*/ -object **getcharplace (object *string, int n, int *shift) { - object **arg = &cdr(string); - int top; - if (sizeof(int) == 4) { top = n>>2; *shift = 3 - (n&3); } - else { top = n>>1; *shift = 1 - (n&1); } - *shift = - (*shift + 2); - for (int i=0; ichars)>>((-shift-2)<<3)) & 0xFF; -} - -/* - gstr - reads a character from a string stream -*/ -int gstr () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - char c = nthchar(GlobalString, GlobalStringIndex++); - if (c != 0) return c; - return '\n'; // -1? -} - -/* - pstr - prints a character to a string stream -*/ -void pstr (char c) { - buildstring(c, &GlobalStringTail); -} - -/* - lispstring - converts a C string to a Lisp string -*/ -object *lispstring (char *s) { - object *obj = newstring(); - object *tail = obj; - while(1) { - char ch = *s++; - if (ch == 0) break; - if (ch == '\\') ch = *s++; - buildstring(ch, &tail); - } - return obj; -} - -/* - stringcompare - a generic string compare function - Used to implement the other string comparison functions. - Returns -1 if the comparison is false, or the index of the first mismatch if it is true. - If lt is true the result is true if the first argument is less than the second argument. - If gt is true the result is true if the first argument is greater than the second argument. - If eq is true the result is true if the first argument is equal to the second argument. -*/ -int stringcompare (object *args, bool lt, bool gt, bool eq) { - object *arg1 = checkstring(first(args)); - object *arg2 = checkstring(second(args)); - arg1 = cdr(arg1); arg2 = cdr(arg2); - int m = 0; chars_t a = 0, b = 0; - while ((arg1 != NULL) || (arg2 != NULL)) { - if (arg1 == NULL) return lt ? m : -1; - if (arg2 == NULL) return gt ? m : -1; - a = arg1->chars; b = arg2->chars; - if (a < b) { if (lt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; } - if (a > b) { if (gt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; } - arg1 = car(arg1); arg2 = car(arg2); - m = m + sizeof(int); - } - if (eq) { m = m - sizeof(int); while (a != 0) { m++; a = a << 8;} return m;} else return -1; -} - -/* - documentation - returns the documentation string of a built-in or user-defined function. -*/ -object *documentation (object *arg, object *env) { - if (arg == NULL) return nil; - if (!symbolp(arg)) error(notasymbol, arg); - object *pair = findpair(arg, env); - if (pair != NULL) { - object *val = cdr(pair); - if (listp(val) && first(val)->name == sym(LAMBDA) && cdr(val) != NULL && cddr(val) != NULL) { - if (stringp(third(val))) return third(val); - } - } - symbol_t docname = arg->name; - if (!builtinp(docname)) return nil; - char *docstring = lookupdoc(builtin(docname)); - if (docstring == NULL) return nil; - object *obj = startstring(); - pfstring(docstring, pstr); - return obj; -} - -/* - apropos - finds the user-defined and built-in functions whose names contain the specified string or symbol, - and prints them if print is true, or returns them in a list. -*/ -object *apropos (object *arg, bool print) { - char buf[17], buf2[33]; - char *part = cstring(princtostring(arg), buf, 17); - object *result = cons(NULL, NULL); - object *ptr = result; - // User-defined? - object *globals = GlobalEnv; - while (globals != NULL) { - object *pair = first(globals); - object *var = car(pair); - object *val = cdr(pair); - char *full = cstring(princtostring(var), buf2, 33); - if (strstr(full, part) != NULL) { - if (print) { - printsymbol(var, pserial); pserial(' '); pserial('('); - if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) pfstring("user function", pserial); - else if (consp(val) && car(val)->type == CODE) pfstring("code", pserial); - else pfstring("user symbol", pserial); - pserial(')'); pln(pserial); - } else { - cdr(ptr) = cons(var, NULL); ptr = cdr(ptr); - } - } - globals = cdr(globals); - testescape(); - } - // Built-in? - int entries = tablesize(0) + tablesize(1); - for (int i = 0; i < entries; i++) { - if (findsubstring(part, (builtin_t)i)) { - if (print) { - uint8_t fntype = getminmax(i)>>6; - pbuiltin((builtin_t)i, pserial); pserial(' '); pserial('('); - if (fntype == FUNCTIONS) pfstring("function", pserial); - else if (fntype == SPECIAL_FORMS) pfstring("special form", pserial); - else pfstring("symbol/keyword", pserial); - pserial(')'); pln(pserial); - } else { - cdr(ptr) = cons(bsymbol(i), NULL); ptr = cdr(ptr); - } - } - testescape(); - } - return cdr(result); -} - -/* - cstring - converts a Lisp string to a C string in buffer and returns buffer - Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word -*/ -char *cstring (object *form, char *buffer, int buflen) { - form = cdr(checkstring(form)); - int index = 0; - while (form != NULL) { - int chars = form->integer; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - char ch = chars>>i & 0xFF; - if (ch) { - if (index >= buflen-1) error2("no room for string"); - buffer[index++] = ch; - } - } - form = car(form); - } - buffer[index] = '\0'; - return buffer; -} - -/* - iptostring - converts a 32-bit IP address to a lisp string -*/ -object *iptostring (uint32_t ip) { - union { uint32_t data2; uint8_t u8[4]; }; - object *obj = startstring(); - data2 = ip; - for (int i=0; i<4; i++) { - if (i) pstr('.'); - pintbase(u8[i], 10, pstr); - } - return obj; -} - -/* - ipstring - parses an IP address from a Lisp string and returns it as an IPAddress type (uint32_t) - Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word -*/ -uint32_t ipstring (object *form) { - form = cdr(checkstring(form)); - int p = 0; - union { uint32_t ipaddress; uint8_t ipbytes[4]; } ; - ipaddress = 0; - while (form != NULL) { - int chars = form->integer; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - char ch = chars>>i & 0xFF; - if (ch) { - if (ch == '.') { p++; if (p > 3) error2("illegal IP address"); } - else ipbytes[p] = (ipbytes[p] * 10) + ch - '0'; - } - } - form = car(form); - } - return ipaddress; -} - -// Lookup variable in environment - -object *value (symbol_t n, object *env) { - while (env != NULL) { - object *pair = car(env); - if (pair != NULL && car(pair)->name == n) return pair; - env = cdr(env); - } - return nil; -} - -/* - findpair - returns the (var . value) pair bound to variable var in the local or global environment -*/ -object *findpair (object *var, object *env) { - symbol_t name = var->name; - object *pair = value(name, env); - if (pair == NULL) pair = value(name, GlobalEnv); - return pair; -} - -/* - boundp - tests whether var is bound to a value -*/ -bool boundp (object *var, object *env) { - if (!symbolp(var)) error(notasymbol, var); - return (findpair(var, env) != NULL); -} - -/* - findvalue - returns the value bound to variable var, or gives an error if unbound -*/ -object *findvalue (object *var, object *env) { - object *pair = findpair(var, env); - if (pair == NULL) error("unknown variable", var); - return pair; -} - -// Handling closures - -object *closure (int tc, symbol_t name, object *function, object *args, object **env) { - object *state = car(function); - function = cdr(function); - int trace = 0; - if (name) trace = tracing(name); - if (trace) { - indent(TraceDepth[trace-1]<<1, ' ', pserial); - pint(TraceDepth[trace-1]++, pserial); - pserial(':'); pserial(' '); pserial('('); printsymbol(symbol(name), pserial); - } - object *params = first(function); - if (!listp(params)) errorsym(name, notalist, params); - function = cdr(function); - // Dropframe - if (tc) { - if (*env != NULL && car(*env) == NULL) { - pop(*env); - while (*env != NULL && car(*env) != NULL) pop(*env); - } else push(nil, *env); - } - // Push state - while (consp(state)) { - object *pair = first(state); - push(pair, *env); - state = cdr(state); - } - // Add arguments to environment - bool optional = false; - while (params != NULL) { - object *value; - object *var = first(params); - if (isbuiltin(var, OPTIONAL)) optional = true; - else { - if (consp(var)) { - if (!optional) errorsym(name, "invalid default value", var); - if (args == NULL) value = eval(second(var), *env); - else { value = first(args); args = cdr(args); } - var = first(var); - if (!symbolp(var)) errorsym(name, "illegal optional parameter", var); - } else if (!symbolp(var)) { - errorsym(name, "illegal function parameter", var); - } else if (isbuiltin(var, AMPREST)) { - params = cdr(params); - var = first(params); - value = args; - args = NULL; - } else { - if (args == NULL) { - if (optional) value = nil; - else errorsym2(name, toofewargs); - } else { value = first(args); args = cdr(args); } - } - push(cons(var,value), *env); - if (trace) { pserial(' '); printobject(value, pserial); } - } - params = cdr(params); - } - if (args != NULL) errorsym2(name, toomanyargs); - if (trace) { pserial(')'); pln(pserial); } - // Do an implicit progn - if (tc) push(nil, *env); - return tf_progn(function, *env); -} - -object *apply (object *function, object *args, object *env) { - if (symbolp(function)) { - builtin_t fname = builtin(function->name); - if ((fname < ENDFUNCTIONS) && ((getminmax(fname)>>6) == FUNCTIONS)) { - Context = fname; - checkargs(args); - return ((fn_ptr_type)lookupfn(fname))(args, env); - } else function = eval(function, env); - } - if (consp(function) && isbuiltin(car(function), LAMBDA)) { - object *result = closure(0, sym(NIL), function, args, &env); - return eval(result, env); - } - if (consp(function) && isbuiltin(car(function), CLOSURE)) { - function = cdr(function); - object *result = closure(0, sym(NIL), function, args, &env); - return eval(result, env); - } - error("illegal function", function); - return NULL; -} - -// In-place operations - -/* - place - returns a pointer to an object referenced in the second argument of an - in-place operation such as setf. bit is used to indicate the bit position in a bit array -*/ -object **place (object *args, object *env, int *bit) { - *bit = -1; - if (atom(args)) return &cdr(findvalue(args, env)); - object* function = first(args); - if (symbolp(function)) { - symbol_t sname = function->name; - if (sname == sym(CAR) || sname == sym(FIRST)) { - object *value = eval(second(args), env); - if (!listp(value)) error(canttakecar, value); - return &car(value); - } - if (sname == sym(CDR) || sname == sym(REST)) { - object *value = eval(second(args), env); - if (!listp(value)) error(canttakecdr, value); - return &cdr(value); - } - if (sname == sym(NTH)) { - int index = checkinteger(eval(second(args), env)); - object *list = eval(third(args), env); - if (atom(list)) { Context = NTH; error("second argument is not a list", list); } - int i = index; - while (i > 0) { - list = cdr(list); - if (list == NULL) { Context = NTH; error(indexrange, number(index)); } - i--; - } - return &car(list); - } - if (sname == sym(CHAR)) { - int index = checkinteger(eval(third(args), env)); - object *string = checkstring(eval(second(args), env)); - object **loc = getcharplace(string, index, bit); - if ((*loc) == NULL || (((((*loc)->chars)>>((-(*bit)-2)<<3)) & 0xFF) == 0)) { Context = CHAR; error(indexrange, number(index)); } - return loc; - } - if (sname == sym(AREF)) { - object *array = eval(second(args), env); - if (!arrayp(array)) { Context = AREF; error("first argument is not an array", array); } - return getarray(array, cddr(args), env, bit); - } - } - error2("illegal place"); - return nil; -} - -// Checked car and cdr - -/* - carx - car with error checking -*/ -object *carx (object *arg) { - if (!listp(arg)) error(canttakecar, arg); - if (arg == nil) return nil; - return car(arg); -} - -/* - cdrx - cdr with error checking -*/ -object *cdrx (object *arg) { - if (!listp(arg)) error(canttakecdr, arg); - if (arg == nil) return nil; - return cdr(arg); -} - -/* - cxxxr - implements a general cxxxr function, - pattern is a sequence of bits 0b1xxx where x is 0 for a and 1 for d. -*/ -object *cxxxr (object *args, uint8_t pattern) { - object *arg = first(args); - while (pattern != 1) { - if ((pattern & 1) == 0) arg = carx(arg); else arg = cdrx(arg); - pattern = pattern>>1; - } - return arg; -} - -// Mapping helper functions - -/* - mapcl - handles either mapc when mapl=false, or mapl when mapl=true -*/ -object *mapcl (object *args, object *env, bool mapl) { - object *function = first(args); - args = cdr(args); - object *result = first(args); - protect(result); - object *params = cons(NULL, NULL); - protect(params); - // Make parameters - while (true) { - object *tailp = params; - object *lists = args; - while (lists != NULL) { - object *list = car(lists); - if (list == NULL) { - unprotect(); unprotect(); - return result; - } - if (improperp(list)) error(notproper, list); - object *item = mapl ? list : first(list); - object *obj = cons(item, NULL); - car(lists) = cdr(list); - cdr(tailp) = obj; tailp = obj; - lists = cdr(lists); - } - apply(function, cdr(params), env); - } -} - -/* - mapcarfun - function specifying how to combine the results in mapcar -*/ -void mapcarfun (object *result, object **tail) { - object *obj = cons(result,NULL); - cdr(*tail) = obj; *tail = obj; -} - -/* - mapcanfun - function specifying how to combine the results in mapcan -*/ -void mapcanfun (object *result, object **tail) { - if (cdr(*tail) != NULL) error(notproper, *tail); - while (consp(result)) { - cdr(*tail) = result; *tail = result; - result = cdr(result); - } -} - -/* - mapcarcan - function used by marcar and mapcan when maplist=false, and maplist when maplist=true - It takes the arguments, the env, a function specifying how the results are combined, and a bool. -*/ -object *mapcarcan (object *args, object *env, mapfun_t fun, bool maplist) { - object *function = first(args); - args = cdr(args); - object *params = cons(NULL, NULL); - protect(params); - object *head = cons(NULL, NULL); - protect(head); - object *tail = head; - // Make parameters - while (true) { - object *tailp = params; - object *lists = args; - while (lists != NULL) { - object *list = car(lists); - if (list == NULL) { - unprotect(); unprotect(); - return cdr(head); - } - if (improperp(list)) error(notproper, list); - object *item = maplist ? list : first(list); - object *obj = cons(item, NULL); - car(lists) = cdr(list); - cdr(tailp) = obj; tailp = obj; - lists = cdr(lists); - } - object *result = apply(function, cdr(params), env); - fun(result, &tail); - } -} - -/* - dobody - function used by do when star=false and do* when star=true -*/ -object *dobody (object *args, object *env, bool star) { - object *varlist = first(args), *endlist = second(args); - object *head = cons(NULL, NULL); - protect(head); - object *ptr = head; - object *newenv = env; - while (varlist != NULL) { - object *varform = first(varlist); - object *var, *init = NULL, *step = NULL; - if (atom(varform)) var = varform; - else { - var = first(varform); - varform = cdr(varform); - if (varform != NULL) { - init = eval(first(varform), env); - varform = cdr(varform); - if (varform != NULL) step = cons(first(varform), NULL); - } - } - object *pair = cons(var, init); - push(pair, newenv); - if (star) env = newenv; - object *cell = cons(cons(step, pair), NULL); - cdr(ptr) = cell; ptr = cdr(ptr); - varlist = cdr(varlist); - } - env = newenv; - head = cdr(head); - object *endtest = first(endlist), *results = cdr(endlist); - while (eval(endtest, env) == NULL) { - object *forms = cddr(args); - while (forms != NULL) { - object *result = eval(car(forms), env); - if (tstflag(RETURNFLAG)) { - clrflag(RETURNFLAG); - return result; - } - forms = cdr(forms); - } - object *varlist = head; - int count = 0; - while (varlist != NULL) { - object *varform = first(varlist); - object *step = car(varform), *pair = cdr(varform); - if (step != NULL) { - object *val = eval(first(step), env); - if (star) { - cdr(pair) = val; - } else { - push(val, GCStack); - push(pair, GCStack); - count++; - } - } - varlist = cdr(varlist); - } - while (count > 0) { - cdr(car(GCStack)) = car(cdr(GCStack)); - pop(GCStack); pop(GCStack); - count--; - } - } - unprotect(); - return eval(tf_progn(results, env), env); -} - -// I2C interface for up to two ports, using Arduino Wire - -void I2Cinit (TwoWire *port, bool enablePullup) { - (void) enablePullup; - port->begin(); -} - -int I2Cread (TwoWire *port) { - return port->read(); -} - -void I2Cwrite (TwoWire *port, uint8_t data) { - port->write(data); -} - -bool I2Cstart (TwoWire *port, uint8_t address, uint8_t read) { - int ok = true; - if (read == 0) { - port->beginTransmission(address); - ok = (port->endTransmission(true) == 0); - port->beginTransmission(address); - } - else port->requestFrom(address, I2Ccount); - return ok; -} - -bool I2Crestart (TwoWire *port, uint8_t address, uint8_t read) { - int error = (port->endTransmission(false) != 0); - if (read == 0) port->beginTransmission(address); - else port->requestFrom(address, I2Ccount); - return error ? false : true; -} - -void I2Cstop (TwoWire *port, uint8_t read) { - if (read == 0) port->endTransmission(); // Check for error? - // Release pins - port->end(); -} - -// Streams - -// Simplify board differences -#if defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) -#define ULISP_I2C1 -#endif - -inline int spiread () { return SPI.transfer(0); } -inline int i2cread () { return I2Cread(&Wire); } -#if defined(ULISP_I2C1) -inline int i2c1read () { return I2Cread(&Wire1); } -#endif -inline int serial1read () { while (!Serial1.available()) testescape(); return Serial1.read(); } -#if defined(sdcardsupport) -File SDpfile, SDgfile; -inline int SDread () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - return SDgfile.read(); -} -#endif - -WiFiClient client; -WiFiServer server(80); - -inline int WiFiread () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - while (!client.available()) testescape(); - return client.read(); -} - -void serialbegin (int address, int baud) { - if (address == 1) Serial1.begin((long)baud*100); - else error("port not supported", number(address)); -} - -void serialend (int address) { - if (address == 1) {Serial1.flush(); Serial1.end(); } - else error("port not supported", number(address)); -} - -gfun_t gstreamfun (object *args) { - int streamtype = SERIALSTREAM; - int address = 0; - gfun_t gfun = gserial; - if (args != NULL) { - int stream = isstream(first(args)); - streamtype = stream>>8; address = stream & 0xFF; - } - if (streamtype == I2CSTREAM) { - if (address < 128) gfun = i2cread; - #if defined(ULISP_I2C1) - else gfun = i2c1read; - #endif - } else if (streamtype == SPISTREAM) gfun = spiread; - else if (streamtype == SERIALSTREAM) { - if (address == 0) gfun = gserial; - else if (address == 1) gfun = serial1read; - } - #if defined(sdcardsupport) - else if (streamtype == SDSTREAM) gfun = (gfun_t)SDread; - #endif - else if (streamtype == WIFISTREAM) gfun = (gfun_t)WiFiread; - else error2("unknown stream type"); - return gfun; -} - -inline void spiwrite (char c) { SPI.transfer(c); } -inline void i2cwrite (char c) { I2Cwrite(&Wire, c); } -#if defined(ULISP_I2C1) -inline void i2c1write (char c) { I2Cwrite(&Wire1, c); } -#endif -inline void serial1write (char c) { Serial1.write(c); } -inline void WiFiwrite (char c) { client.write(c); } -#if defined(sdcardsupport) -inline void SDwrite (char c) { SDpfile.write(c); } -#endif -#if defined(gfxsupport) -inline void gfxwrite (char c) { tft.write(c); } -#endif - -pfun_t pstreamfun (object *args) { - int streamtype = SERIALSTREAM; - int address = 0; - pfun_t pfun = pserial; - if (args != NULL && first(args) != NULL) { - int stream = isstream(first(args)); - streamtype = stream>>8; address = stream & 0xFF; - } - if (streamtype == I2CSTREAM) { - if (address < 128) pfun = i2cwrite; - #if defined(ULISP_I2C1) - else pfun = i2c1write; - #endif - } else if (streamtype == SPISTREAM) pfun = spiwrite; - else if (streamtype == SERIALSTREAM) { - if (address == 0) pfun = pserial; - else if (address == 1) pfun = serial1write; - } - else if (streamtype == STRINGSTREAM) { - pfun = pstr; - } - #if defined(sdcardsupport) - else if (streamtype == SDSTREAM) pfun = (pfun_t)SDwrite; - #endif - #if defined(gfxsupport) - else if (streamtype == GFXSTREAM) pfun = (pfun_t)gfxwrite; - #endif - else if (streamtype == WIFISTREAM) pfun = (pfun_t)WiFiwrite; - else error2("unknown stream type"); - return pfun; -} - -// Check pins - -void checkanalogread (int pin) { -#if defined(ESP32) || defined(ARDUINO_ESP32_DEV) - if (!(pin==0 || pin==2 || pin==4 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=36) || pin==39)) - error("invalid pin", number(pin)); -#elif defined(ARDUINO_FEATHER_ESP32) - if (!(pin==4 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=36) || pin==39)) - error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) - if (!(pin==8 || (pin>=14 && pin<=18))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) - if (!(pin==4 || pin==7 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=33))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) - if (!((pin>=5 && pin<=9) || (pin>=16 && pin<=18))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) - if (!((pin>=0 && pin<=1) || (pin>=3 && pin<=5))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_FEATHERS2) | defined(ARDUINO_ESP32S2_DEV) - if (!((pin>=1 && pin<=20))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ESP32C3_DEV) - if (!((pin>=0 && pin<=5))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ESP32S3_DEV) - if (!((pin>=1 && pin<=20))) error("invalid pin", number(pin)); -#endif -} - -void checkanalogwrite (int pin) { -#if defined(ESP32) || defined(ARDUINO_FEATHER_ESP32) || defined(ARDUINO_ESP32_DEV) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) - if (!(pin>=25 && pin<=26)) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) || defined(ARDUINO_FEATHERS2) || defined(ARDUINO_ESP32S2_DEV) - if (!(pin>=17 && pin<=18)) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ESP32C3_DEV) | defined(ARDUINO_ESP32S3_DEV) | defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) - error2(ANALOGWRITE, "not supported"); -#endif -} - -// Note - -void tone (int pin, int note) { - (void) pin, (void) note; -} - -void noTone (int pin) { - (void) pin; -} - -const int scale[] = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902}; - -void playnote (int pin, int note, int octave) { - int oct = octave + note/12; - int prescaler = 8 - oct; - if (prescaler<0 || prescaler>8) error("octave out of range", number(oct)); - tone(pin, scale[note%12]>>prescaler); -} - -void nonote (int pin) { - noTone(pin); -} - -// Sleep - -void initsleep () { } - -void doze (int secs) { - delay(1000 * secs); -} - -// Prettyprint - -const int PPINDENT = 2; -const int PPWIDTH = 80; -const int GFXPPWIDTH = 52; // 320 pixel wide screen -int ppwidth = PPWIDTH; - -void pcount (char c) { - if (c == '\n') PrintCount++; - PrintCount++; -} - -/* - atomwidth - calculates the character width of an atom -*/ -uint8_t atomwidth (object *obj) { - PrintCount = 0; - printobject(obj, pcount); - return PrintCount; -} - -/* - basewidth - calculates the character width of an integer printed in a given base -*/ -uint8_t basewidth (object *obj, uint8_t base) { - PrintCount = 0; - pintbase(obj->integer, base, pcount); - return PrintCount; -} - -/* - quoted - tests whether an object is quoted -*/ -bool quoted (object *obj) { - return (consp(obj) && car(obj) != NULL && car(obj)->name == sym(QUOTE) && consp(cdr(obj)) && cddr(obj) == NULL); -} - -/* - subwidth - returns the space left from w after printing object -*/ -int subwidth (object *obj, int w) { - if (atom(obj)) return w - atomwidth(obj); - if (quoted(obj)) obj = car(cdr(obj)); - return subwidthlist(obj, w - 1); -} - -/* - subwidth - returns the space left from w after printing a list -*/ -int subwidthlist (object *form, int w) { - while (form != NULL && w >= 0) { - if (atom(form)) return w - (2 + atomwidth(form)); - w = subwidth(car(form), w - 1); - form = cdr(form); - } - return w; -} - -/* - superprint - handles pretty-printing -*/ -void superprint (object *form, int lm, pfun_t pfun) { - if (atom(form)) { - if (symbolp(form) && form->name == sym(NOTHING)) printsymbol(form, pfun); - else printobject(form, pfun); - } else if (quoted(form)) { - pfun('\''); - superprint(car(cdr(form)), lm + 1, pfun); - } else { - lm = lm + PPINDENT; - bool fits = (subwidth(form, ppwidth - lm - PPINDENT) >= 0); - int special = 0, extra = 0; bool separate = true; - object *arg = car(form); - if (symbolp(arg) && builtinp(arg->name)) { - uint8_t minmax = getminmax(builtin(arg->name)); - if (minmax == 0327 || minmax == 0313) special = 2; // defun, setq, setf, defvar - else if (minmax == 0317 || minmax == 0017 || minmax == 0117 || minmax == 0123) special = 1; - } - while (form != NULL) { - if (atom(form)) { pfstring(" . ", pfun); printobject(form, pfun); pfun(')'); return; } - else if (separate) { - pfun('('); - separate = false; - } else if (special) { - pfun(' '); - special--; - } else if (fits) { - pfun(' '); - } else { pln(pfun); indent(lm, ' ', pfun); } - superprint(car(form), lm+extra, pfun); - form = cdr(form); - } - pfun(')'); - } -} - -/* - edit - the Lisp tree editor - Steps through a function definition, editing it a bit at a time, using single-key editing commands. -*/ -object *edit (object *fun) { - while (1) { - if (tstflag(EXITEDITOR)) return fun; - char c = gserial(); - if (c == 'q') setflag(EXITEDITOR); - else if (c == 'b') return fun; - else if (c == 'r') fun = read(gserial); - else if (c == '\n') { pfl(pserial); superprint(fun, 0, pserial); pln(pserial); } - else if (c == 'c') fun = cons(read(gserial), fun); - else if (atom(fun)) pserial('!'); - else if (c == 'd') fun = cons(car(fun), edit(cdr(fun))); - else if (c == 'a') fun = cons(edit(car(fun)), cdr(fun)); - else if (c == 'x') fun = cdr(fun); - else pserial('?'); - } -} - -// Special forms - -object *sp_quote (object *args, object *env) { - (void) env; - return first(args); -} - -/* - (or item*) - Evaluates its arguments until one returns non-nil, and returns its value. -*/ -object *sp_or (object *args, object *env) { - while (args != NULL) { - object *val = eval(car(args), env); - if (val != NULL) return val; - args = cdr(args); - } - return nil; -} - -/* - (defun name (parameters) form*) - Defines a function. -*/ -object *sp_defun (object *args, object *env) { - (void) env; - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - object *val = cons(bsymbol(LAMBDA), cdr(args)); - object *pair = value(var->name, GlobalEnv); - if (pair != NULL) cdr(pair) = val; - else push(cons(var, val), GlobalEnv); - return var; -} - -/* - (defvar variable form) - Defines a global variable. -*/ -object *sp_defvar (object *args, object *env) { - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - object *val = NULL; - args = cdr(args); - if (args != NULL) { setflag(NOESC); val = eval(first(args), env); clrflag(NOESC); } - object *pair = value(var->name, GlobalEnv); - if (pair != NULL) cdr(pair) = val; - else push(cons(var, val), GlobalEnv); - return var; -} - -/* - (setq symbol value [symbol value]*) - For each pair of arguments assigns the value of the second argument - to the variable specified in the first argument. -*/ -object *sp_setq (object *args, object *env) { - object *arg = nil; - while (args != NULL) { - if (cdr(args) == NULL) error2(oddargs); - object *pair = findvalue(first(args), env); - arg = eval(second(args), env); - cdr(pair) = arg; - args = cddr(args); - } - return arg; -} - -/* - (loop forms*) - Executes its arguments repeatedly until one of the arguments calls (return), - which then causes an exit from the loop. -*/ -object *sp_loop (object *args, object *env) { - object *start = args; - for (;;) { - yield(); - args = start; - while (args != NULL) { - object *result = eval(car(args),env); - if (tstflag(RETURNFLAG)) { - clrflag(RETURNFLAG); - return result; - } - args = cdr(args); - } - testescape(); - } -} - -/* - (push item place) - Modifies the value of place, which should be a list, to add item onto the front of the list, - and returns the new list. -*/ -object *sp_push (object *args, object *env) { - int bit; - object *item = eval(first(args), env); - object **loc = place(second(args), env, &bit); - if (bit != -1) error2(invalidarg); - push(item, *loc); - return *loc; -} - -/* - (pop place) - Modifies the value of place, which should be a non-nil list, to remove its first item, - and returns that item. -*/ -object *sp_pop (object *args, object *env) { - int bit; - object *arg = first(args); - if (arg == NULL) error2(invalidarg); - object **loc = place(arg, env, &bit); - if (bit < -1) error(invalidarg, arg); - if (!consp(*loc)) error(notalist, *loc); - object *result = car(*loc); - pop(*loc); - return result; -} - -// Accessors - -/* - (incf place [number]) - Increments a place, which should have an numeric value, and returns the result. - The third argument is an optional increment which defaults to 1. -*/ -object *sp_incf (object *args, object *env) { - int bit; - object **loc = place(first(args), env, &bit); - if (bit < -1) error2(notanumber); - args = cdr(args); - - object *x = *loc; - object *inc = (args != NULL) ? eval(first(args), env) : NULL; - - if (bit != -1) { - int increment; - if (inc == NULL) increment = 1; else increment = checkbitvalue(inc); - int newvalue = (((*loc)->integer)>>bit & 1) + increment; - - if (newvalue & ~1) error2("result is not a bit value"); - *loc = number((((*loc)->integer) & ~(1<integer; - - if (inc == NULL) increment = 1; else increment = inc->integer; - - if (increment < 1) { - if (INT_MIN - increment > value) *loc = makefloat((float)value + (float)increment); - else *loc = number(value + increment); - } else { - if (INT_MAX - increment < value) *loc = makefloat((float)value + (float)increment); - else *loc = number(value + increment); - } - } else error2(notanumber); - return *loc; -} - -/* - (decf place [number]) - Decrements a place, which should have an numeric value, and returns the result. - The third argument is an optional decrement which defaults to 1. -*/ -object *sp_decf (object *args, object *env) { - int bit; - object **loc = place(first(args), env, &bit); - if (bit < -1) error2(notanumber); - args = cdr(args); - - object *x = *loc; - object *dec = (args != NULL) ? eval(first(args), env) : NULL; - - if (bit != -1) { - int decrement; - if (dec == NULL) decrement = 1; else decrement = checkbitvalue(dec); - int newvalue = (((*loc)->integer)>>bit & 1) - decrement; - - if (newvalue & ~1) error2("result is not a bit value"); - *loc = number((((*loc)->integer) & ~(1<integer; - - if (dec == NULL) decrement = 1; else decrement = dec->integer; - - if (decrement < 1) { - if (INT_MAX + decrement < value) *loc = makefloat((float)value - (float)decrement); - else *loc = number(value - decrement); - } else { - if (INT_MIN + decrement > value) *loc = makefloat((float)value - (float)decrement); - else *loc = number(value - decrement); - } - } else error2(notanumber); - return *loc; -} - -/* - (setf place value [place value]*) - For each pair of arguments modifies a place to the result of evaluating value. -*/ -object *sp_setf (object *args, object *env) { - int bit; - object *arg = nil; - while (args != NULL) { - if (cdr(args) == NULL) error2(oddargs); - object **loc = place(first(args), env, &bit); - arg = eval(second(args), env); - if (bit == -1) *loc = arg; - else if (bit < -1) (*loc)->chars = ((*loc)->chars & ~(0xff<<((-bit-2)<<3))) | checkchar(arg)<<((-bit-2)<<3); - else *loc = number((checkinteger(*loc) & ~(1<name); - args = cdr(args); - } - int i = 0; - while (i < TRACEMAX) { - if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args); - i++; - } - return args; -} - -/* - (untrace [function]*) - Turns off tracing of up to TRACEMAX user-defined functions, and returns a list of the functions untraced. - If no functions are specified it untraces all functions. -*/ -object *sp_untrace (object *args, object *env) { - (void) env; - if (args == NULL) { - int i = 0; - while (i < TRACEMAX) { - if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args); - TraceFn[i] = 0; - i++; - } - } else { - while (args != NULL) { - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - untrace(var->name); - args = cdr(args); - } - } - return args; -} - -/* - (for-millis ([number]) form*) - Executes the forms and then waits until a total of number milliseconds have elapsed. - Returns the total number of milliseconds taken. -*/ -object *sp_formillis (object *args, object *env) { - object *param = checkarguments(args, 0, 1); - unsigned long start = millis(); - unsigned long now, total = 0; - if (param != NULL) total = checkinteger(eval(first(param), env)); - eval(tf_progn(cdr(args),env), env); - do { - now = millis() - start; - testescape(); - } while (now < total); - if (now <= INT_MAX) return number(now); - return nil; -} - -/* - (time form) - Prints the value returned by the form, and the time taken to evaluate the form - in milliseconds or seconds. -*/ -object *sp_time (object *args, object *env) { - unsigned long start = millis(); - object *result = eval(first(args), env); - unsigned long elapsed = millis() - start; - printobject(result, pserial); - pfstring("\nTime: ", pserial); - if (elapsed < 1000) { - pint(elapsed, pserial); - pfstring(" ms\n", pserial); - } else { - elapsed = elapsed+50; - pint(elapsed/1000, pserial); - pserial('.'); pint((elapsed/100)%10, pserial); - pfstring(" s\n", pserial); - } - return bsymbol(NOTHING); -} - -/* - (with-output-to-string (str) form*) - Returns a string containing the output to the stream variable str. -*/ -object *sp_withoutputtostring (object *args, object *env) { - object *params = checkarguments(args, 1, 1); - object *var = first(params); - object *pair = cons(var, stream(STRINGSTREAM, 0)); - push(pair,env); - object *string = startstring(); - protect(string); - object *forms = cdr(args); - eval(tf_progn(forms,env), env); - unprotect(); - return string; -} - -/* - (with-serial (str port [baud]) form*) - Evaluates the forms with str bound to a serial-stream using port. - The optional baud gives the baud rate divided by 100, default 96. -*/ -object *sp_withserial (object *args, object *env) { - object *params = checkarguments(args, 2, 3); - object *var = first(params); - int address = checkinteger(eval(second(params), env)); - params = cddr(params); - int baud = 96; - if (params != NULL) baud = checkinteger(eval(first(params), env)); - object *pair = cons(var, stream(SERIALSTREAM, address)); - push(pair,env); - serialbegin(address, baud); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - serialend(address); - return result; -} - -/* - (with-i2c (str [port] address [read-p]) form*) - Evaluates the forms with str bound to an i2c-stream defined by address. - If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes - to be read from the stream. If port is omitted it defaults to 0, otherwise it specifies the port, 0 or 1. -*/ -object *sp_withi2c (object *args, object *env) { - object *params = checkarguments(args, 2, 4); - object *var = first(params); - int address = checkinteger(eval(second(params), env)); - params = cddr(params); - if ((address == 0 || address == 1) && params != NULL) { - address = address * 128 + checkinteger(eval(first(params), env)); - params = cdr(params); - } - int read = 0; // Write - I2Ccount = 0; - if (params != NULL) { - object *rw = eval(first(params), env); - if (integerp(rw)) I2Ccount = rw->integer; - read = (rw != NULL); - } - // Top bit of address is I2C port - TwoWire *port = &Wire; - #if defined(ULISP_I2C1) - if (address > 127) port = &Wire1; - #endif - I2Cinit(port, 1); // Pullups - object *pair = cons(var, (I2Cstart(port, address & 0x7F, read)) ? stream(I2CSTREAM, address) : nil); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - I2Cstop(port, read); - return result; -} - -/* - (with-sd-card (str filename [mode]) form*) - Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename. - If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite. -*/ -object *sp_withsdcard (object *args, object *env) { - #if defined(sdcardsupport) - object *params = checkarguments(args, 2, 3); - object *var = first(params); - params = cdr(params); - if (params == NULL) error2("no filename specified"); - builtin_t temp = Context; - object *filename = eval(first(params), env); - Context = temp; - if (!stringp(filename)) error("filename is not a string", filename); - params = cdr(params); - SD.begin(); - int mode = 0; - if (params != NULL && first(params) != NULL) mode = checkinteger(first(params)); - const char *oflag = FILE_READ; - if (mode == 1) oflag = FILE_APPEND; else if (mode == 2) oflag = FILE_WRITE; - if (mode >= 1) { - char buffer[BUFFERSIZE]; - SDpfile = SD.open(MakeFilename(filename, buffer), oflag); - if (!SDpfile) error2("problem writing to SD card or invalid filename"); - } else { - char buffer[BUFFERSIZE]; - SDgfile = SD.open(MakeFilename(filename, buffer), oflag); - if (!SDgfile) error2("problem reading from SD card or invalid filename"); - } - object *pair = cons(var, stream(SDSTREAM, 1)); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - if (mode >= 1) SDpfile.close(); else SDgfile.close(); - return result; - #else - (void) args, (void) env; - error2("not supported"); - return nil; - #endif -} - -// Tail-recursive forms - -/* - (progn form*) - Evaluates several forms grouped together into a block, and returns the result of evaluating the last form. -*/ -object *tf_progn (object *args, object *env) { - if (args == NULL) return nil; - object *more = cdr(args); - while (more != NULL) { - object *result = eval(car(args),env); - if (tstflag(RETURNFLAG)) return quote(result); - args = more; - more = cdr(args); - } - return car(args); -} - -/* - (if test then [else]) - Evaluates test. If it's non-nil the form then is evaluated and returned; - otherwise the form else is evaluated and returned. -*/ -object *tf_if (object *args, object *env) { - if (args == NULL || cdr(args) == NULL) error2(toofewargs); - if (eval(first(args), env) != nil) return second(args); - args = cddr(args); - return (args != NULL) ? first(args) : nil; -} - -/* - (cond ((test form*) (test form*) ... )) - Each argument is a list consisting of a test optionally followed by one or more forms. - If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond. - If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way. -*/ -object *tf_cond (object *args, object *env) { - while (args != NULL) { - object *clause = first(args); - if (!consp(clause)) error(illegalclause, clause); - object *test = eval(first(clause), env); - object *forms = cdr(clause); - if (test != nil) { - if (forms == NULL) return quote(test); else return tf_progn(forms, env); - } - args = cdr(args); - } - return nil; -} - -/* - (when test form*) - Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned. -*/ -object *tf_when (object *args, object *env) { - if (args == NULL) error2(noargument); - if (eval(first(args), env) != nil) return tf_progn(cdr(args),env); - else return nil; -} - -/* - (unless test form*) - Evaluates the test. If it's nil the forms are evaluated and the last value is returned. -*/ -object *tf_unless (object *args, object *env) { - if (args == NULL) error2(noargument); - if (eval(first(args), env) != nil) return nil; - else return tf_progn(cdr(args),env); -} - -/* - (case keyform ((key form*) (key form*) ... )) - Evaluates a keyform to produce a test key, and then tests this against a series of arguments, - each of which is a list containing a key optionally followed by one or more forms. -*/ -object *tf_case (object *args, object *env) { - object *test = eval(first(args), env); - args = cdr(args); - while (args != NULL) { - object *clause = first(args); - if (!consp(clause)) error(illegalclause, clause); - object *key = car(clause); - object *forms = cdr(clause); - if (consp(key)) { - while (key != NULL) { - if (eq(test,car(key))) return tf_progn(forms, env); - key = cdr(key); - } - } else if (eq(test,key) || eq(key,tee)) return tf_progn(forms, env); - args = cdr(args); - } - return nil; -} - -/* - (and item*) - Evaluates its arguments until one returns nil, and returns the last value. -*/ -object *tf_and (object *args, object *env) { - if (args == NULL) return tee; - object *more = cdr(args); - while (more != NULL) { - if (eval(car(args), env) == NULL) return nil; - args = more; - more = cdr(args); - } - return car(args); -} - -// Core functions - -/* - (not item) - Returns t if its argument is nil, or nil otherwise. Equivalent to null. -*/ -object *fn_not (object *args, object *env) { - (void) env; - return (first(args) == nil) ? tee : nil; -} - -/* - (cons item item) - If the second argument is a list, cons returns a new list with item added to the front of the list. - If the second argument isn't a list cons returns a dotted pair. -*/ -object *fn_cons (object *args, object *env) { - (void) env; - return cons(first(args), second(args)); -} - -/* - (atom item) - Returns t if its argument is a single number, symbol, or nil. -*/ -object *fn_atom (object *args, object *env) { - (void) env; - return atom(first(args)) ? tee : nil; -} - -/* - (listp item) - Returns t if its argument is a list. -*/ -object *fn_listp (object *args, object *env) { - (void) env; - return listp(first(args)) ? tee : nil; -} - -/* - (consp item) - Returns t if its argument is a non-null list. -*/ -object *fn_consp (object *args, object *env) { - (void) env; - return consp(first(args)) ? tee : nil; -} - -/* - (symbolp item) - Returns t if its argument is a symbol. -*/ -object *fn_symbolp (object *args, object *env) { - (void) env; - object *arg = first(args); - return (arg == NULL || symbolp(arg)) ? tee : nil; -} - -/* - (arrayp item) - Returns t if its argument is an array. -*/ -object *fn_arrayp (object *args, object *env) { - (void) env; - return arrayp(first(args)) ? tee : nil; -} - -/* - (boundp item) - Returns t if its argument is a symbol with a value. -*/ -object *fn_boundp (object *args, object *env) { - return boundp(first(args), env) ? tee : nil; -} - -/* - (keywordp item) - Returns t if its argument is a built-in or user-defined keyword. -*/ -object *fn_keywordp (object *args, object *env) { - (void) env; - object *arg = first(args); - if (!symbolp(arg)) return nil; - return (keywordp(arg) || colonp(arg->name)) ? tee : nil; -} - -/* - (set symbol value [symbol value]*) - For each pair of arguments, assigns the value of the second argument to the value of the first argument. -*/ -object *fn_setfn (object *args, object *env) { - object *arg = nil; - while (args != NULL) { - if (cdr(args) == NULL) error2(oddargs); - object *pair = findvalue(first(args), env); - arg = second(args); - cdr(pair) = arg; - args = cddr(args); - } - return arg; -} - -/* - (streamp item) - Returns t if its argument is a stream. -*/ -object *fn_streamp (object *args, object *env) { - (void) env; - object *arg = first(args); - return streamp(arg) ? tee : nil; -} - -/* - (eq item item) - Tests whether the two arguments are the same symbol, same character, equal numbers, - or point to the same cons, and returns t or nil as appropriate. -*/ -object *fn_eq (object *args, object *env) { - (void) env; - return eq(first(args), second(args)) ? tee : nil; -} - -/* - (equal item item) - Tests whether the two arguments are the same symbol, same character, equal numbers, - or point to the same cons, and returns t or nil as appropriate. -*/ -object *fn_equal (object *args, object *env) { - (void) env; - return equal(first(args), second(args)) ? tee : nil; -} - -// List functions - -/* - (car list) - Returns the first item in a list. -*/ -object *fn_car (object *args, object *env) { - (void) env; - return carx(first(args)); -} - -/* - (cdr list) - Returns a list with the first item removed. -*/ -object *fn_cdr (object *args, object *env) { - (void) env; - return cdrx(first(args)); -} - -/* - (caar list) -*/ -object *fn_caar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b100); -} - -/* - (cadr list) -*/ -object *fn_cadr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b101); -} - -/* - (cdar list) - Equivalent to (cdr (car list)). -*/ -object *fn_cdar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b110); -} - -/* - (cddr list) - Equivalent to (cdr (cdr list)). -*/ -object *fn_cddr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b111); -} - -/* - (caaar list) - Equivalent to (car (car (car list))). -*/ -object *fn_caaar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1000); -} - -/* - (caadr list) - Equivalent to (car (car (cdar list))). -*/ -object *fn_caadr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1001);; -} - -/* - (cadar list) - Equivalent to (car (cdr (car list))). -*/ -object *fn_cadar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1010); -} - -/* - (caddr list) - Equivalent to (car (cdr (cdr list))). -*/ -object *fn_caddr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1011); -} - -/* - (cdaar list) - Equivalent to (cdar (car (car list))). -*/ -object *fn_cdaar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1100); -} - -/* - (cdadr list) - Equivalent to (cdr (car (cdr list))). -*/ -object *fn_cdadr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1101); -} - -/* - (cddar list) - Equivalent to (cdr (cdr (car list))). -*/ -object *fn_cddar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1110); -} - -/* - (cdddr list) - Equivalent to (cdr (cdr (cdr list))). -*/ -object *fn_cdddr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1111); -} - -/* - (length item) - Returns the number of items in a list, the length of a string, or the length of a one-dimensional array. -*/ -object *fn_length (object *args, object *env) { - (void) env; - object *arg = first(args); - if (listp(arg)) return number(listlength(arg)); - if (stringp(arg)) return number(stringlength(arg)); - if (!(arrayp(arg) && cdr(cddr(arg)) == NULL)) error("argument is not a list, 1d array, or string", arg); - return number(abs(first(cddr(arg))->integer)); -} - -/* - (array-dimensions item) - Returns a list of the dimensions of an array. -*/ -object *fn_arraydimensions (object *args, object *env) { - (void) env; - object *array = first(args); - if (!arrayp(array)) error("argument is not an array", array); - object *dimensions = cddr(array); - return (first(dimensions)->integer < 0) ? cons(number(-(first(dimensions)->integer)), cdr(dimensions)) : dimensions; -} - -/* - (list item*) - Returns a list of the values of its arguments. -*/ -object *fn_list (object *args, object *env) { - (void) env; - return args; -} - -/* - (copy-list list) - Returns a copy of a list. -*/ -object *fn_copylist (object *args, object *env) { - (void) env; - object *arg = first(args); - if (!listp(arg)) error(notalist, arg); - object *result = cons(NULL, NULL); - object *ptr = result; - while (arg != NULL) { - cdr(ptr) = cons(car(arg), NULL); - ptr = cdr(ptr); arg = cdr(arg); - } - return cdr(result); -} - -/* - (make-array size [:initial-element element] [:element-type 'bit]) - If size is an integer it creates a one-dimensional array with elements from 0 to size-1. - If size is a list of n integers it creates an n-dimensional array with those dimensions. - If :element-type 'bit is specified the array is a bit array. -*/ -object *fn_makearray (object *args, object *env) { - (void) env; - object *def = nil; - bool bitp = false; - object *dims = first(args); - if (dims == NULL) error2("dimensions can't be nil"); - else if (atom(dims)) dims = cons(dims, NULL); - args = cdr(args); - while (args != NULL && cdr(args) != NULL) { - object *var = first(args); - if (isbuiltin(first(args), INITIALELEMENT)) def = second(args); - else if (isbuiltin(first(args), ELEMENTTYPE) && isbuiltin(second(args), BIT)) bitp = true; - else error("argument not recognised", var); - args = cddr(args); - } - if (bitp) { - if (def == nil) def = number(0); - else def = number(-checkbitvalue(def)); // 1 becomes all ones - } - return makearray(dims, def, bitp); -} - -/* - (reverse list) - Returns a list with the elements of list in reverse order. -*/ -object *fn_reverse (object *args, object *env) { - (void) env; - object *list = first(args); - object *result = NULL; - while (list != NULL) { - if (improperp(list)) error(notproper, list); - push(first(list),result); - list = cdr(list); - } - return result; -} - -/* - (nth number list) - Returns the nth item in list, counting from zero. -*/ -object *fn_nth (object *args, object *env) { - (void) env; - int n = checkinteger(first(args)); - if (n < 0) error(indexnegative, first(args)); - object *list = second(args); - while (list != NULL) { - if (improperp(list)) error(notproper, list); - if (n == 0) return car(list); - list = cdr(list); - n--; - } - return nil; -} - -/* - (aref array index [index*]) - Returns an element from the specified array. -*/ -object *fn_aref (object *args, object *env) { - (void) env; - int bit; - object *array = first(args); - if (!arrayp(array)) error("first argument is not an array", array); - object *loc = *getarray(array, cdr(args), 0, &bit); - if (bit == -1) return loc; - else return number((loc->integer)>>bit & 1); -} - -/* - (assoc key list [:test function]) - Looks up a key in an association list of (key . value) pairs, using eq or the specified test function, - and returns the matching pair, or nil if no pair is found. -*/ -object *fn_assoc (object *args, object *env) { - (void) env; - object *key = first(args); - object *list = second(args); - object *test = testargument(cddr(args)); - while (list != NULL) { - if (improperp(list)) error(notproper, list); - object *pair = first(list); - if (!listp(pair)) error("element is not a list", pair); - if (pair != NULL && apply(test, cons(key, cons(car(pair), NULL)), env) != NULL) return pair; - list = cdr(list); - } - return nil; -} - -/* - (member item list [:test function]) - Searches for an item in a list, using eq or the specified test function, and returns the list starting - from the first occurrence of the item, or nil if it is not found. -*/ -object *fn_member (object *args, object *env) { - (void) env; - object *item = first(args); - object *list = second(args); - object *test = testargument(cddr(args)); - while (list != NULL) { - if (improperp(list)) error(notproper, list); - if (apply(test, cons(item, cons(car(list), NULL)), env) != NULL) return list; - list = cdr(list); - } - return nil; -} - -/* - (apply function list) - Returns the result of evaluating function, with the list of arguments specified by the second parameter. -*/ -object *fn_apply (object *args, object *env) { - object *previous = NULL; - object *last = args; - while (cdr(last) != NULL) { - previous = last; - last = cdr(last); - } - object *arg = car(last); - if (!listp(arg)) error(notalist, arg); - cdr(previous) = arg; - return apply(first(args), cdr(args), env); -} - -/* - (funcall function argument*) - Evaluates function with the specified arguments. -*/ -object *fn_funcall (object *args, object *env) { - return apply(first(args), cdr(args), env); -} - -/* - (append list*) - Joins its arguments, which should be lists, into a single list. -*/ -object *fn_append (object *args, object *env) { - (void) env; - object *head = NULL; - object *tail; - while (args != NULL) { - object *list = first(args); - if (!listp(list)) error(notalist, list); - while (consp(list)) { - object *obj = cons(car(list), cdr(list)); - if (head == NULL) head = obj; - else cdr(tail) = obj; - tail = obj; - list = cdr(list); - if (cdr(args) != NULL && improperp(list)) error(notproper, first(args)); - } - args = cdr(args); - } - return head; -} - -/* - (mapc function list1 [list]*) - Applies the function to each element in one or more lists, ignoring the results. - It returns the first list argument. -*/ -object *fn_mapc (object *args, object *env) { - return mapcl(args, env, false); -} - -/* - (mapl function list1 [list]*) - Applies the function to one or more lists and then successive cdrs of those lists, - ignoring the results. It returns the first list argument. -*/ -object *fn_mapl (object *args, object *env) { - return mapcl(args, env, true); -} - -/* - (mapcar function list1 [list]*) - Applies the function to each element in one or more lists, and returns the resulting list. -*/ -object *fn_mapcar (object *args, object *env) { - return mapcarcan(args, env, mapcarfun, false); -} - -/* - (mapcan function list1 [list]*) - Applies the function to each element in one or more lists. The results should be lists, - and these are destructively concatenated together to give the value returned. -*/ -object *fn_mapcan (object *args, object *env) { - return mapcarcan(args, env, mapcanfun, false); -} - -/* - (maplist function list1 [list]*) - Applies the function to one or more lists and then successive cdrs of those lists, - and returns the resulting list. -*/ -object *fn_maplist (object *args, object *env) { - return mapcarcan(args, env, mapcarfun, true); -} - -/* - (mapcon function list1 [list]*) - Applies the function to one or more lists and then successive cdrs of those lists, - and these are destructively concatenated together to give the value returned. -*/ -object *fn_mapcon (object *args, object *env) { - return mapcarcan(args, env, mapcanfun, true); -} - -// Arithmetic functions - -/* - (+ number*) - Adds its arguments together. - If each argument is an integer, and the running total doesn't overflow, the result is an integer, - otherwise a floating-point number. -*/ -object *fn_add (object *args, object *env) { - (void) env; - int result = 0; - while (args != NULL) { - object *arg = car(args); - if (floatp(arg)) return add_floats(args, (float)result); - else if (integerp(arg)) { - int val = arg->integer; - if (val < 1) { if (INT_MIN - val > result) return add_floats(args, (float)result); } - else { if (INT_MAX - val < result) return add_floats(args, (float)result); } - result = result + val; - } else error(notanumber, arg); - args = cdr(args); - } - return number(result); -} - -/* - (- number*) - If there is one argument, negates the argument. - If there are two or more arguments, subtracts the second and subsequent arguments from the first argument. - If each argument is an integer, and the running total doesn't overflow, returns the result as an integer, - otherwise a floating-point number. -*/ -object *fn_subtract (object *args, object *env) { - (void) env; - object *arg = car(args); - args = cdr(args); - if (args == NULL) return negate(arg); - else if (floatp(arg)) return subtract_floats(args, arg->single_float); - else if (integerp(arg)) { - int result = arg->integer; - while (args != NULL) { - arg = car(args); - if (floatp(arg)) return subtract_floats(args, result); - else if (integerp(arg)) { - int val = (car(args))->integer; - if (val < 1) { if (INT_MAX + val < result) return subtract_floats(args, result); } - else { if (INT_MIN + val > result) return subtract_floats(args, result); } - result = result - val; - } else error(notanumber, arg); - args = cdr(args); - } - return number(result); - } else error(notanumber, arg); - return nil; -} - -/* - (* number*) - Multiplies its arguments together. - If each argument is an integer, and the running total doesn't overflow, the result is an integer, - otherwise it's a floating-point number. -*/ -object *fn_multiply (object *args, object *env) { - (void) env; - int result = 1; - while (args != NULL){ - object *arg = car(args); - if (floatp(arg)) return multiply_floats(args, result); - else if (integerp(arg)) { - int64_t val = result * (int64_t)(arg->integer); - if ((val > INT_MAX) || (val < INT_MIN)) return multiply_floats(args, result); - result = val; - } else error(notanumber, arg); - args = cdr(args); - } - return number(result); -} - -/* - (/ number*) - Divides the first argument by the second and subsequent arguments. - If each argument is an integer, and each division produces an exact result, the result is an integer; - otherwise it's a floating-point number. -*/ -object *fn_divide (object *args, object *env) { - (void) env; - object* arg = first(args); - args = cdr(args); - // One argument - if (args == NULL) { - if (floatp(arg)) { - float f = arg->single_float; - if (f == 0.0) error2("division by zero"); - return makefloat(1.0 / f); - } else if (integerp(arg)) { - int i = arg->integer; - if (i == 0) error2("division by zero"); - else if (i == 1) return number(1); - else return makefloat(1.0 / i); - } else error(notanumber, arg); - } - // Multiple arguments - if (floatp(arg)) return divide_floats(args, arg->single_float); - else if (integerp(arg)) { - int result = arg->integer; - while (args != NULL) { - arg = car(args); - if (floatp(arg)) { - return divide_floats(args, result); - } else if (integerp(arg)) { - int i = arg->integer; - if (i == 0) error2("division by zero"); - if ((result % i) != 0) return divide_floats(args, result); - if ((result == INT_MIN) && (i == -1)) return divide_floats(args, result); - result = result / i; - args = cdr(args); - } else error(notanumber, arg); - } - return number(result); - } else error(notanumber, arg); - return nil; -} - -/* - (mod number number) - Returns its first argument modulo the second argument. - If both arguments are integers the result is an integer; otherwise it's a floating-point number. -*/ -object *fn_mod (object *args, object *env) { - (void) env; - object *arg1 = first(args); - object *arg2 = second(args); - if (integerp(arg1) && integerp(arg2)) { - int divisor = arg2->integer; - if (divisor == 0) error2("division by zero"); - int dividend = arg1->integer; - int remainder = dividend % divisor; - if ((dividend<0) != (divisor<0)) remainder = remainder + divisor; - return number(remainder); - } else { - float fdivisor = checkintfloat(arg2); - if (fdivisor == 0.0) error2("division by zero"); - float fdividend = checkintfloat(arg1); - float fremainder = fmod(fdividend , fdivisor); - if ((fdividend<0) != (fdivisor<0)) fremainder = fremainder + fdivisor; - return makefloat(fremainder); - } -} - -/* - (1+ number) - Adds one to its argument and returns it. - If the argument is an integer the result is an integer if possible; - otherwise it's a floating-point number. -*/ -object *fn_oneplus (object *args, object *env) { - (void) env; - object* arg = first(args); - if (floatp(arg)) return makefloat((arg->single_float) + 1.0); - else if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MAX) return makefloat((arg->integer) + 1.0); - else return number(result + 1); - } else error(notanumber, arg); - return nil; -} - -/* - (1- number) - Subtracts one from its argument and returns it. - If the argument is an integer the result is an integer if possible; - otherwise it's a floating-point number. -*/ -object *fn_oneminus (object *args, object *env) { - (void) env; - object* arg = first(args); - if (floatp(arg)) return makefloat((arg->single_float) - 1.0); - else if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MIN) return makefloat((arg->integer) - 1.0); - else return number(result - 1); - } else error(notanumber, arg); - return nil; -} - -/* - (abs number) - Returns the absolute, positive value of its argument. - If the argument is an integer the result will be returned as an integer if possible, - otherwise a floating-point number. -*/ -object *fn_abs (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return makefloat(abs(arg->single_float)); - else if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MIN) return makefloat(abs((float)result)); - else return number(abs(result)); - } else error(notanumber, arg); - return nil; -} - -/* - (random number) - If number is an integer returns a random number between 0 and one less than its argument. - Otherwise returns a floating-point number between zero and number. -*/ -object *fn_random (object *args, object *env) { - (void) env; - object *arg = first(args); - if (integerp(arg)) return number(random(arg->integer)); - else if (floatp(arg)) return makefloat((float)rand()/(float)(RAND_MAX/(arg->single_float))); - else error(notanumber, arg); - return nil; -} - -/* - (max number*) - Returns the maximum of one or more arguments. -*/ -object *fn_maxfn (object *args, object *env) { - (void) env; - object* result = first(args); - args = cdr(args); - while (args != NULL) { - object *arg = car(args); - if (integerp(result) && integerp(arg)) { - if ((arg->integer) > (result->integer)) result = arg; - } else if ((checkintfloat(arg) > checkintfloat(result))) result = arg; - args = cdr(args); - } - return result; -} - -/* - (min number*) - Returns the minimum of one or more arguments. -*/ -object *fn_minfn (object *args, object *env) { - (void) env; - object* result = first(args); - args = cdr(args); - while (args != NULL) { - object *arg = car(args); - if (integerp(result) && integerp(arg)) { - if ((arg->integer) < (result->integer)) result = arg; - } else if ((checkintfloat(arg) < checkintfloat(result))) result = arg; - args = cdr(args); - } - return result; -} - -// Arithmetic comparisons - -/* - (/= number*) - Returns t if none of the arguments are equal, or nil if two or more arguments are equal. -*/ -object *fn_noteq (object *args, object *env) { - (void) env; - while (args != NULL) { - object *nargs = args; - object *arg1 = first(nargs); - nargs = cdr(nargs); - while (nargs != NULL) { - object *arg2 = first(nargs); - if (integerp(arg1) && integerp(arg2)) { - if ((arg1->integer) == (arg2->integer)) return nil; - } else if ((checkintfloat(arg1) == checkintfloat(arg2))) return nil; - nargs = cdr(nargs); - } - args = cdr(args); - } - return tee; -} - -/* - (= number*) - Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise. -*/ -object *fn_numeq (object *args, object *env) { - (void) env; - return compare(args, false, false, true); -} - -/* - (< number*) - Returns t if each argument is less than the next argument, and nil otherwise. -*/ -object *fn_less (object *args, object *env) { - (void) env; - return compare(args, true, false, false); -} - -/* - (<= number*) - Returns t if each argument is less than or equal to the next argument, and nil otherwise. -*/ -object *fn_lesseq (object *args, object *env) { - (void) env; - return compare(args, true, false, true); -} - -/* - (> number*) - Returns t if each argument is greater than the next argument, and nil otherwise. -*/ -object *fn_greater (object *args, object *env) { - (void) env; - return compare(args, false, true, false); -} - -/* - (>= number*) - Returns t if each argument is greater than or equal to the next argument, and nil otherwise. -*/ -object *fn_greatereq (object *args, object *env) { - (void) env; - return compare(args, false, true, true); -} - -/* - (plusp number) - Returns t if the argument is greater than zero, or nil otherwise. -*/ -object *fn_plusp (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return ((arg->single_float) > 0.0) ? tee : nil; - else if (integerp(arg)) return ((arg->integer) > 0) ? tee : nil; - else error(notanumber, arg); - return nil; -} - -/* - (minusp number) - Returns t if the argument is less than zero, or nil otherwise. -*/ -object *fn_minusp (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return ((arg->single_float) < 0.0) ? tee : nil; - else if (integerp(arg)) return ((arg->integer) < 0) ? tee : nil; - else error(notanumber, arg); - return nil; -} - -/* - (zerop number) - Returns t if the argument is zero. -*/ -object *fn_zerop (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return ((arg->single_float) == 0.0) ? tee : nil; - else if (integerp(arg)) return ((arg->integer) == 0) ? tee : nil; - else error(notanumber, arg); - return nil; -} - -/* - (oddp number) - Returns t if the integer argument is odd. -*/ -object *fn_oddp (object *args, object *env) { - (void) env; - int arg = checkinteger(first(args)); - return ((arg & 1) == 1) ? tee : nil; -} - -/* - (evenp number) - Returns t if the integer argument is even. -*/ -object *fn_evenp (object *args, object *env) { - (void) env; - int arg = checkinteger(first(args)); - return ((arg & 1) == 0) ? tee : nil; -} - -// Number functions - -/* - (integerp number) - Returns t if the argument is an integer. -*/ -object *fn_integerp (object *args, object *env) { - (void) env; - return integerp(first(args)) ? tee : nil; -} - -/* - (numberp number) - Returns t if the argument is a number. -*/ -object *fn_numberp (object *args, object *env) { - (void) env; - object *arg = first(args); - return (integerp(arg) || floatp(arg)) ? tee : nil; -} - -// Floating-point functions - -/* - (float number) - Returns its argument converted to a floating-point number. -*/ -object *fn_floatfn (object *args, object *env) { - (void) env; - object *arg = first(args); - return (floatp(arg)) ? arg : makefloat((float)(arg->integer)); -} - -/* - (floatp number) - Returns t if the argument is a floating-point number. -*/ -object *fn_floatp (object *args, object *env) { - (void) env; - return floatp(first(args)) ? tee : nil; -} - -/* - (sin number) - Returns sin(number). -*/ -object *fn_sin (object *args, object *env) { - (void) env; - return makefloat(sin(checkintfloat(first(args)))); -} - -/* - (cos number) - Returns cos(number). -*/ -object *fn_cos (object *args, object *env) { - (void) env; - return makefloat(cos(checkintfloat(first(args)))); -} - -/* - (tan number) - Returns tan(number). -*/ -object *fn_tan (object *args, object *env) { - (void) env; - return makefloat(tan(checkintfloat(first(args)))); -} - -/* - (asin number) - Returns asin(number). -*/ -object *fn_asin (object *args, object *env) { - (void) env; - return makefloat(asin(checkintfloat(first(args)))); -} - -/* - (acos number) - Returns acos(number). -*/ -object *fn_acos (object *args, object *env) { - (void) env; - return makefloat(acos(checkintfloat(first(args)))); -} - -/* - (atan number1 [number2]) - Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1. -*/ -object *fn_atan (object *args, object *env) { - (void) env; - object *arg = first(args); - float div = 1.0; - args = cdr(args); - if (args != NULL) div = checkintfloat(first(args)); - return makefloat(atan2(checkintfloat(arg), div)); -} - -/* - (sinh number) - Returns sinh(number). -*/ -object *fn_sinh (object *args, object *env) { - (void) env; - return makefloat(sinh(checkintfloat(first(args)))); -} - -/* - (cosh number) - Returns cosh(number). -*/ -object *fn_cosh (object *args, object *env) { - (void) env; - return makefloat(cosh(checkintfloat(first(args)))); -} - -/* - (tanh number) - Returns tanh(number). -*/ -object *fn_tanh (object *args, object *env) { - (void) env; - return makefloat(tanh(checkintfloat(first(args)))); -} - -/* - (exp number) - Returns exp(number). -*/ -object *fn_exp (object *args, object *env) { - (void) env; - return makefloat(exp(checkintfloat(first(args)))); -} - -/* - (sqrt number) - Returns sqrt(number). -*/ -object *fn_sqrt (object *args, object *env) { - (void) env; - return makefloat(sqrt(checkintfloat(first(args)))); -} - -/* - (log number [base]) - Returns the logarithm of number to the specified base. If base is omitted it defaults to e. -*/ -object *fn_log (object *args, object *env) { - (void) env; - object *arg = first(args); - float fresult = log(checkintfloat(arg)); - args = cdr(args); - if (args == NULL) return makefloat(fresult); - else return makefloat(fresult / log(checkintfloat(first(args)))); -} - -/* - (expt number power) - Returns number raised to the specified power. - Returns the result as an integer if the arguments are integers and the result will be within range, - otherwise a floating-point number. -*/ -object *fn_expt (object *args, object *env) { - (void) env; - object *arg1 = first(args); object *arg2 = second(args); - float float1 = checkintfloat(arg1); - float value = log(abs(float1)) * checkintfloat(arg2); - if (integerp(arg1) && integerp(arg2) && ((arg2->integer) >= 0) && (abs(value) < 21.4875)) - return number(intpower(arg1->integer, arg2->integer)); - if (float1 < 0) { - if (integerp(arg2)) return makefloat((arg2->integer & 1) ? -exp(value) : exp(value)); - else error2("invalid result"); - } - return makefloat(exp(value)); -} - -/* - (ceiling number [divisor]) - Returns ceil(number/divisor). If omitted, divisor is 1. -*/ -object *fn_ceiling (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number(ceil(checkintfloat(arg) / checkintfloat(first(args)))); - else return number(ceil(checkintfloat(arg))); -} - -/* - (floor number [divisor]) - Returns floor(number/divisor). If omitted, divisor is 1. -*/ -object *fn_floor (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number(floor(checkintfloat(arg) / checkintfloat(first(args)))); - else return number(floor(checkintfloat(arg))); -} - -/* - (truncate number [divisor]) - Returns the integer part of number/divisor. If divisor is omitted it defaults to 1. -*/ -object *fn_truncate (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number((int)(checkintfloat(arg) / checkintfloat(first(args)))); - else return number((int)(checkintfloat(arg))); -} - -/* - (round number [divisor]) - Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1. -*/ -object *fn_round (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number(round(checkintfloat(arg) / checkintfloat(first(args)))); - else return number(round(checkintfloat(arg))); -} - -// Characters - -/* - (char string n) - Returns the nth character in a string, counting from zero. -*/ -object *fn_char (object *args, object *env) { - (void) env; - object *arg = first(args); - if (!stringp(arg)) error(notastring, arg); - object *n = second(args); - char c = nthchar(arg, checkinteger(n)); - if (c == 0) error(indexrange, n); - return character(c); -} - -/* - (char-code character) - Returns the ASCII code for a character, as an integer. -*/ -object *fn_charcode (object *args, object *env) { - (void) env; - return number(checkchar(first(args))); -} - -/* - (code-char integer) - Returns the character for the specified ASCII code. -*/ -object *fn_codechar (object *args, object *env) { - (void) env; - return character(checkinteger(first(args))); -} - -/* - (characterp item) - Returns t if the argument is a character and nil otherwise. -*/ -object *fn_characterp (object *args, object *env) { - (void) env; - return characterp(first(args)) ? tee : nil; -} - -// Strings - -/* - (stringp item) - Returns t if the argument is a string and nil otherwise. -*/ -object *fn_stringp (object *args, object *env) { - (void) env; - return stringp(first(args)) ? tee : nil; -} - -/* - (string= string string) - Returns t if the two strings are the same, or nil otherwise. -*/ -object *fn_stringeq (object *args, object *env) { - (void) env; - int m = stringcompare(args, false, false, true); - return m == -1 ? nil : tee; -} - -/* - (string< string string) - Returns the index to the first mismatch if the first string is alphabetically less than the second string, - or nil otherwise. -*/ -object *fn_stringless (object *args, object *env) { - (void) env; - int m = stringcompare(args, true, false, false); - return m == -1 ? nil : number(m); -} - -/* - (string> string string) - Returns the index to the first mismatch if the first string is alphabetically greater than the second string, - or nil otherwise. -*/ -object *fn_stringgreater (object *args, object *env) { - (void) env; - int m = stringcompare(args, false, true, false); - return m == -1 ? nil : number(m); -} - -/* - (string/= string string) - Returns the index to the first mismatch if the two strings are not the same, or nil otherwise. -*/ -object *fn_stringnoteq (object *args, object *env) { - (void) env; - int m = stringcompare(args, true, true, false); - return m == -1 ? nil : number(m); -} - -/* - (string<= string string) - Returns the index to the first mismatch if the first string is alphabetically less than or equal to - the second string, or nil otherwise. -*/ -object *fn_stringlesseq (object *args, object *env) { - (void) env; - int m = stringcompare(args, true, false, true); - return m == -1 ? nil : number(m); -} - -/* - (string>= string string) - Returns the index to the first mismatch if the first string is alphabetically greater than or equal to - the second string, or nil otherwise. -*/ -object *fn_stringgreatereq (object *args, object *env) { - (void) env; - int m = stringcompare(args, false, true, true); - return m == -1 ? nil : number(m); -} - -/* - (sort list test) - Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list. -*/ -object *fn_sort (object *args, object *env) { - if (first(args) == NULL) return nil; - object *list = cons(nil,first(args)); - protect(list); - object *predicate = second(args); - object *compare = cons(NULL, cons(NULL, NULL)); - protect(compare); - object *ptr = cdr(list); - while (cdr(ptr) != NULL) { - object *go = list; - while (go != ptr) { - car(compare) = car(cdr(ptr)); - car(cdr(compare)) = car(cdr(go)); - if (apply(predicate, compare, env)) break; - go = cdr(go); - } - if (go != ptr) { - object *obj = cdr(ptr); - cdr(ptr) = cdr(obj); - cdr(obj) = cdr(go); - cdr(go) = obj; - } else ptr = cdr(ptr); - } - unprotect(); unprotect(); - return cdr(list); -} - -/* - (string item) - Converts its argument to a string. -*/ -object *fn_stringfn (object *args, object *env) { - return fn_princtostring(args, env); -} - -/* - (concatenate 'string string*) - Joins together the strings given in the second and subsequent arguments, and returns a single string. -*/ -object *fn_concatenate (object *args, object *env) { - (void) env; - object *arg = first(args); - if (builtin(arg->name) != STRINGFN) error2("only supports strings"); - args = cdr(args); - object *result = newstring(); - object *tail = result; - while (args != NULL) { - object *obj = checkstring(first(args)); - obj = cdr(obj); - while (obj != NULL) { - int quad = obj->chars; - while (quad != 0) { - char ch = quad>>((sizeof(int)-1)*8) & 0xFF; - buildstring(ch, &tail); - quad = quad<<8; - } - obj = car(obj); - } - args = cdr(args); - } - return result; -} - -/* - (subseq seq start [end]) - Returns a subsequence of a list or string from item start to item end-1. -*/ -object *fn_subseq (object *args, object *env) { - (void) env; - object *arg = first(args); - int start = checkinteger(second(args)), end; - if (start < 0) error(indexnegative, second(args)); - args = cddr(args); - if (listp(arg)) { - int length = listlength(arg); - if (args != NULL) end = checkinteger(car(args)); else end = length; - if (start > end || end > length) error2(indexrange); - object *result = cons(NULL, NULL); - object *ptr = result; - for (int x = 0; x < end; x++) { - if (x >= start) { cdr(ptr) = cons(car(arg), NULL); ptr = cdr(ptr); } - arg = cdr(arg); - } - return cdr(result); - } else if (stringp(arg)) { - int length = stringlength(arg); - if (args != NULL) end = checkinteger(car(args)); else end = length; - if (start > end || end > length) error2(indexrange); - object *result = newstring(); - object *tail = result; - for (int i=start; i= 0) return number(value << count); - else return number(value >> abs(count)); -} - -/* - (logbitp bit value) - Returns t if bit number bit in value is a '1', and nil if it is a '0'. -*/ -object *fn_logbitp (object *args, object *env) { - (void) env; - int index = checkinteger(first(args)); - int value = checkinteger(second(args)); - return (bitRead(value, index) == 1) ? tee : nil; -} - -// System functions - -/* - (eval form*) - Evaluates its argument an extra time. -*/ -object *fn_eval (object *args, object *env) { - return eval(first(args), env); -} - -/* - (return [value]) - Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value. -*/ -object *fn_return (object *args, object *env) { - (void) env; - setflag(RETURNFLAG); - if (args == NULL) return nil; else return first(args); -} - -/* - (globals) - Returns a list of global variables. -*/ -object *fn_globals (object *args, object *env) { - (void) args, (void) env; - object *result = cons(NULL, NULL); - object *ptr = result; - object *arg = GlobalEnv; - while (arg != NULL) { - cdr(ptr) = cons(car(car(arg)), NULL); ptr = cdr(ptr); - arg = cdr(arg); - } - return cdr(result); -} - -/* - (locals) - Returns an association list of local variables and their values. -*/ -object *fn_locals (object *args, object *env) { - (void) args; - return env; -} - -/* - (makunbound symbol) - Removes the value of the symbol from GlobalEnv and returns the symbol. -*/ -object *fn_makunbound (object *args, object *env) { - (void) env; - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - delassoc(var, &GlobalEnv); - return var; -} - -/* - (break) - Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL. -*/ -object *fn_break (object *args, object *env) { - (void) args; - pfstring("\nBreak!\n", pserial); - BreakLevel++; - repl(env); - BreakLevel--; - return nil; -} - -/* - (read [stream]) - Reads an atom or list from the serial input and returns it. - If stream is specified the item is read from the specified stream. -*/ -object *fn_read (object *args, object *env) { - (void) env; - gfun_t gfun = gstreamfun(args); - return read(gfun); -} - -/* - (prin1 item [stream]) - Prints its argument, and returns its value. - Strings are printed with quotation marks and escape characters. -*/ -object *fn_prin1 (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - printobject(obj, pfun); - return obj; -} - -/* - (print item [stream]) - Prints its argument with quotation marks and escape characters, on a new line, and followed by a space. - If stream is specified the argument is printed to the specified stream. -*/ -object *fn_print (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - pln(pfun); - printobject(obj, pfun); - pfun(' '); - return obj; -} - -/* - (princ item [stream]) - Prints its argument, and returns its value. - Characters and strings are printed without quotation marks or escape characters. -*/ -object *fn_princ (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - prin1object(obj, pfun); - return obj; -} - -/* - (terpri [stream]) - Prints a new line, and returns nil. - If stream is specified the new line is written to the specified stream. -*/ -object *fn_terpri (object *args, object *env) { - (void) env; - pfun_t pfun = pstreamfun(args); - pln(pfun); - return nil; -} - -/* - (read-byte stream) - Reads a byte from a stream and returns it. -*/ -object *fn_readbyte (object *args, object *env) { - (void) env; - gfun_t gfun = gstreamfun(args); - int c = gfun(); - return (c == -1) ? nil : number(c); -} - -/* - (read-line [stream]) - Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline. - If stream is specified the line is read from the specified stream. -*/ -object *fn_readline (object *args, object *env) { - (void) env; - gfun_t gfun = gstreamfun(args); - return readstring('\n', false, gfun); -} - -/* - (write-byte number [stream]) - Writes a byte to a stream. -*/ -object *fn_writebyte (object *args, object *env) { - (void) env; - int value = checkinteger(first(args)); - pfun_t pfun = pstreamfun(cdr(args)); - (pfun)(value); - return nil; -} - -/* - (write-string string [stream]) - Writes a string. If stream is specified the string is written to the stream. -*/ -object *fn_writestring (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - char temp = Flags; - clrflag(PRINTREADABLY); - printstring(obj, pfun); - Flags = temp; - return nil; -} - -/* - (write-line string [stream]) - Writes a string terminated by a newline character. If stream is specified the string is written to the stream. -*/ -object *fn_writeline (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - char temp = Flags; - clrflag(PRINTREADABLY); - printstring(obj, pfun); - pln(pfun); - Flags = temp; - return nil; -} - -/* - (restart-i2c stream [read-p]) - Restarts an i2c-stream. - If read-p is nil or omitted the stream is written to. - If read-p is an integer it specifies the number of bytes to be read from the stream. -*/ -object *fn_restarti2c (object *args, object *env) { - (void) env; - int stream = isstream(first(args)); - args = cdr(args); - int read = 0; // Write - I2Ccount = 0; - if (args != NULL) { - object *rw = first(args); - if (integerp(rw)) I2Ccount = rw->integer; - read = (rw != NULL); - } - int address = stream & 0xFF; - if (stream>>8 != I2CSTREAM) error2("not an i2c stream"); - TwoWire *port; - if (address < 128) port = &Wire; - #if defined(ULISP_I2C1) - else port = &Wire1; - #endif - return I2Crestart(port, address & 0x7F, read) ? tee : nil; -} - -/* - (gc) - Forces a garbage collection and prints the number of objects collected, and the time taken. -*/ -object *fn_gc (object *obj, object *env) { - int initial = Freespace; - unsigned long start = micros(); - gc(obj, env); - unsigned long elapsed = micros() - start; - pfstring("Space: ", pserial); - pint(Freespace - initial, pserial); - pfstring(" bytes, Time: ", pserial); - pint(elapsed, pserial); - pfstring(" us\n", pserial); - return nil; -} - -/* - (room) - Returns the number of free Lisp cells remaining. -*/ -object *fn_room (object *args, object *env) { - (void) args, (void) env; - return number(Freespace); -} - -/* - (save-image [symbol]) - Saves the current uLisp image to non-volatile memory or SD card so it can be loaded using load-image. -*/ -object *fn_saveimage (object *args, object *env) { - if (args != NULL) args = eval(first(args), env); - return number(saveimage(args)); -} - -/* - (load-image [filename]) - Loads a saved uLisp image from non-volatile memory or SD card. -*/ -object *fn_loadimage (object *args, object *env) { - (void) env; - if (args != NULL) args = first(args); - return number(loadimage(args)); -} - -/* - (cls) - Prints a clear-screen character. -*/ -object *fn_cls (object *args, object *env) { - (void) args, (void) env; - pserial(12); - return nil; -} - -// Arduino procedures - -/* - (pinmode pin mode) - Sets the input/output mode of an Arduino pin number, and returns nil. - The mode parameter can be an integer, a keyword, or t or nil. -*/ -object *fn_pinmode (object *args, object *env) { - (void) env; int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(first(args)); - int pm = INPUT; - arg = second(args); - if (keywordp(arg)) pm = checkkeyword(arg); - else if (integerp(arg)) { - int mode = arg->integer; - if (mode == 1) pm = OUTPUT; else if (mode == 2) pm = INPUT_PULLUP; - #if defined(INPUT_PULLDOWN) - else if (mode == 4) pm = INPUT_PULLDOWN; - #endif - } else if (arg != nil) pm = OUTPUT; - pinMode(pin, pm); - return nil; -} - -/* - (digitalread pin) - Reads the state of the specified Arduino pin number and returns t (high) or nil (low). -*/ -object *fn_digitalread (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(arg); - if (digitalRead(pin) != 0) return tee; else return nil; -} - -/* - (digitalwrite pin state) - Sets the state of the specified Arduino pin number. -*/ -object *fn_digitalwrite (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(arg); - arg = second(args); - int mode; - if (keywordp(arg)) mode = checkkeyword(arg); - else if (integerp(arg)) mode = arg->integer ? HIGH : LOW; - else mode = (arg != nil) ? HIGH : LOW; - digitalWrite(pin, mode); - return arg; -} - -/* - (analogread pin) - Reads the specified Arduino analogue pin number and returns the value. -*/ -object *fn_analogread (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else { - pin = checkinteger(arg); - checkanalogread(pin); - } - return number(analogRead(pin)); -} - -/* - (analogreadresolution bits) - Specifies the resolution for the analogue inputs on platforms that support it. - The default resolution on all platforms is 10 bits. -*/ -object *fn_analogreadresolution (object *args, object *env) { - (void) env; - object *arg = first(args); - #if defined(ESP32) - analogReadResolution(checkinteger(arg)); - #else - error2("not supported"); - #endif - return arg; -} - -/* - (analogwrite pin value) - Writes the value to the specified Arduino pin number. -*/ -object *fn_analogwrite (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(arg); - checkanalogwrite(pin); - object *value = second(args); - analogWrite(pin, checkinteger(value)); - return value; -} - -/* - (delay number) - Delays for a specified number of milliseconds. -*/ -object *fn_delay (object *args, object *env) { - (void) env; - object *arg1 = first(args); - unsigned long start = millis(); - unsigned long total = checkinteger(arg1); - do testescape(); - while (millis() - start < total); - return arg1; -} - -/* - (millis) - Returns the time in milliseconds that uLisp has been running. -*/ -object *fn_millis (object *args, object *env) { - (void) args, (void) env; - return number(millis()); -} - -/* - (sleep secs) - Puts the processor into a low-power sleep mode for secs. - Only supported on some platforms. On other platforms it does delay(1000*secs). -*/ -object *fn_sleep (object *args, object *env) { - (void) env; - object *arg1 = first(args); - doze(checkinteger(arg1)); - return arg1; -} - -/* - (note [pin] [note] [octave]) - Generates a square wave on pin. - note represents the note in the well-tempered scale. - The argument octave can specify an octave; default 0. -*/ -object *fn_note (object *args, object *env) { - (void) env; - static int pin = 255; - if (args != NULL) { - pin = checkinteger(first(args)); - int note = 48, octave = 0; - if (cdr(args) != NULL) { - note = checkinteger(second(args)); - if (cddr(args) != NULL) octave = checkinteger(third(args)); - } - playnote(pin, note, octave); - } else nonote(pin); - return nil; -} - -/* - (register address [value]) - Reads or writes the value of a peripheral register. - If value is not specified the function returns the value of the register at address. - If value is specified the value is written to the register at address and the function returns value. -*/ -object *fn_register (object *args, object *env) { - (void) env; - object *arg = first(args); - int addr; - if (keywordp(arg)) addr = checkkeyword(arg); - else addr = checkinteger(first(args)); - if (cdr(args) == NULL) return number(*(uint32_t *)addr); - (*(uint32_t *)addr) = checkinteger(second(args)); - return second(args); -} - -// Tree Editor - -/* - (edit 'function) - Calls the Lisp tree editor to allow you to edit a function definition. -*/ -object *fn_edit (object *args, object *env) { - object *fun = first(args); - object *pair = findvalue(fun, env); - clrflag(EXITEDITOR); - object *arg = edit(eval(fun, env)); - cdr(pair) = arg; - return arg; -} - -// Pretty printer - -/* - (pprint item [str]) - Prints its argument, using the pretty printer, to display it formatted in a structured way. - If str is specified it prints to the specified stream. It returns no value. -*/ -object *fn_pprint (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - #if defined(gfxsupport) - if (pfun == gfxwrite) ppwidth = GFXPPWIDTH; - #endif - pln(pfun); - superprint(obj, 0, pfun); - ppwidth = PPWIDTH; - return bsymbol(NOTHING); -} - -/* - (pprintall [str]) - Pretty-prints the definition of every function and variable defined in the uLisp workspace. - If str is specified it prints to the specified stream. It returns no value. -*/ -object *fn_pprintall (object *args, object *env) { - (void) env; - pfun_t pfun = pstreamfun(args); - #if defined(gfxsupport) - if (pfun == gfxwrite) ppwidth = GFXPPWIDTH; - #endif - object *globals = GlobalEnv; - while (globals != NULL) { - object *pair = first(globals); - object *var = car(pair); - object *val = cdr(pair); - pln(pfun); - if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) { - superprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun); - } else { - superprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun); - } - pln(pfun); - testescape(); - globals = cdr(globals); - } - ppwidth = PPWIDTH; - return bsymbol(NOTHING); -} - -// Format - -/* - (format output controlstring [arguments]*) - Outputs its arguments formatted according to the format directives in controlstring. -*/ -object *fn_format (object *args, object *env) { - (void) env; - pfun_t pfun = pserial; - object *output = first(args); - object *obj; - if (output == nil) { obj = startstring(); pfun = pstr; } - else if (output != tee) pfun = pstreamfun(args); - object *formatstr = checkstring(second(args)); - object *save = NULL; - args = cddr(args); - int len = stringlength(formatstr); - uint8_t n = 0, width = 0, w, bra = 0; - char pad = ' '; - bool tilde = false, mute = false, comma = false, quote = false; - while (n < len) { - char ch = nthchar(formatstr, n); - char ch2 = ch & ~0x20; // force to upper case - if (tilde) { - if (ch == '}') { - if (save == NULL) formaterr(formatstr, "no matching ~{", n); - if (args == NULL) { args = cdr(save); save = NULL; } else n = bra; - mute = false; tilde = false; - } - else if (!mute) { - if (comma && quote) { pad = ch; comma = false, quote = false; } - else if (ch == '\'') { - if (comma) quote = true; - else formaterr(formatstr, "quote not valid", n); - } - else if (ch == '~') { pfun('~'); tilde = false; } - else if (ch >= '0' && ch <= '9') width = width*10 + ch - '0'; - else if (ch == ',') comma = true; - else if (ch == '%') { pln(pfun); tilde = false; } - else if (ch == '&') { pfl(pfun); tilde = false; } - else if (ch == '^') { - if (save != NULL && args == NULL) mute = true; - tilde = false; - } - else if (ch == '{') { - if (save != NULL) formaterr(formatstr, "can't nest ~{", n); - if (args == NULL) formaterr(formatstr, noargument, n); - if (!listp(first(args))) formaterr(formatstr, notalist, n); - save = args; args = first(args); bra = n; tilde = false; - if (args == NULL) mute = true; - } - else if (ch2 == 'A' || ch2 == 'S' || ch2 == 'D' || ch2 == 'G' || ch2 == 'X' || ch2 == 'B') { - if (args == NULL) formaterr(formatstr, noargument, n); - object *arg = first(args); args = cdr(args); - uint8_t aw = atomwidth(arg); - if (width < aw) w = 0; else w = width-aw; - tilde = false; - if (ch2 == 'A') { prin1object(arg, pfun); indent(w, pad, pfun); } - else if (ch2 == 'S') { printobject(arg, pfun); indent(w, pad, pfun); } - else if (ch2 == 'D' || ch2 == 'G') { indent(w, pad, pfun); prin1object(arg, pfun); } - else if (ch2 == 'X' || ch2 == 'B') { - if (integerp(arg)) { - uint8_t base = (ch2 == 'B') ? 2 : 16; - uint8_t hw = basewidth(arg, base); if (width < hw) w = 0; else w = width-hw; - indent(w, pad, pfun); pintbase(arg->integer, base, pfun); - } else { - indent(w, pad, pfun); prin1object(arg, pfun); - } - } - tilde = false; - } else formaterr(formatstr, "invalid directive", n); - } - } else { - if (ch == '~') { tilde = true; pad = ' '; width = 0; comma = false; quote = false; } - else if (!mute) pfun(ch); - } - n++; - } - if (output == nil) return obj; - else return nil; -} - -// LispLibrary - -/* - (require 'symbol) - Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library. - It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library. -*/ -object *fn_require (object *args, object *env) { - object *arg = first(args); - object *globals = GlobalEnv; - if (!symbolp(arg)) error(notasymbol, arg); - while (globals != NULL) { - object *pair = first(globals); - object *var = car(pair); - if (symbolp(var) && var == arg) return nil; - globals = cdr(globals); - } - GlobalStringIndex = 0; - object *line = read(glibrary); - while (line != NULL) { - // Is this the definition we want - symbol_t fname = first(line)->name; - if ((fname == sym(DEFUN) || fname == sym(DEFVAR)) && symbolp(second(line)) && second(line)->name == arg->name) { - eval(line, env); - return tee; - } - line = read(glibrary); - } - return nil; -} - -/* - (list-library) - Prints a list of the functions defined in the List Library. -*/ -object *fn_listlibrary (object *args, object *env) { - (void) args, (void) env; - GlobalStringIndex = 0; - object *line = read(glibrary); - while (line != NULL) { - builtin_t bname = builtin(first(line)->name); - if (bname == DEFUN || bname == DEFVAR) { - printsymbol(second(line), pserial); pserial(' '); - } - line = read(glibrary); - } - return bsymbol(NOTHING); -} - -// Documentation - -/* - (? item) - Prints the documentation string of a built-in or user-defined function. -*/ -object *sp_help (object *args, object *env) { - if (args == NULL) error2(noargument); - object *docstring = documentation(first(args), env); - if (docstring) { - char temp = Flags; - clrflag(PRINTREADABLY); - printstring(docstring, pserial); - Flags = temp; - } - return bsymbol(NOTHING); -} - -/* - (documentation 'symbol [type]) - Returns the documentation string of a built-in or user-defined function. The type argument is ignored. -*/ -object *fn_documentation (object *args, object *env) { - return documentation(first(args), env); -} - -/* - (apropos item) - Prints the user-defined and built-in functions whose names contain the specified string or symbol. -*/ -object *fn_apropos (object *args, object *env) { - (void) env; - apropos(first(args), true); - return bsymbol(NOTHING); -} - -/* - (apropos-list item) - Returns a list of user-defined and built-in functions whose names contain the specified string or symbol. -*/ -object *fn_aproposlist (object *args, object *env) { - (void) env; - return apropos(first(args), false); -} - -// Error handling - -/* - (unwind-protect form1 [forms]*) - Evaluates form1 and forms in order and returns the value of form1, - but guarantees to evaluate forms even if an error occurs in form1. -*/ -object *sp_unwindprotect (object *args, object *env) { - if (args == NULL) error2(toofewargs); - object *current_GCStack = GCStack; - jmp_buf dynamic_handler; - jmp_buf *previous_handler = handler; - handler = &dynamic_handler; - object *protected_form = first(args); - object *result; - - bool signaled = false; - if (!setjmp(dynamic_handler)) { - result = eval(protected_form, env); - } else { - GCStack = current_GCStack; - signaled = true; - } - handler = previous_handler; - - object *protective_forms = cdr(args); - while (protective_forms != NULL) { - eval(car(protective_forms), env); - if (tstflag(RETURNFLAG)) break; - protective_forms = cdr(protective_forms); - } - - if (!signaled) return result; - GCStack = NULL; - longjmp(*handler, 1); -} - -/* - (ignore-errors [forms]*) - Evaluates forms ignoring errors. -*/ -object *sp_ignoreerrors (object *args, object *env) { - object *current_GCStack = GCStack; - jmp_buf dynamic_handler; - jmp_buf *previous_handler = handler; - handler = &dynamic_handler; - object *result = nil; - - bool muffled = tstflag(MUFFLEERRORS); - setflag(MUFFLEERRORS); - bool signaled = false; - if (!setjmp(dynamic_handler)) { - while (args != NULL) { - result = eval(car(args), env); - if (tstflag(RETURNFLAG)) break; - args = cdr(args); - } - } else { - GCStack = current_GCStack; - signaled = true; - } - handler = previous_handler; - if (!muffled) clrflag(MUFFLEERRORS); - - if (signaled) return bsymbol(NOTHING); - else return result; -} - -/* - (error controlstring [arguments]*) - Signals an error. The message is printed by format using the controlstring and arguments. -*/ -object *sp_error (object *args, object *env) { - object *message = eval(cons(bsymbol(FORMAT), cons(nil, args)), env); - if (!tstflag(MUFFLEERRORS)) { - char temp = Flags; - clrflag(PRINTREADABLY); - pfstring("Error: ", pserial); printstring(message, pserial); - Flags = temp; - pln(pserial); - } - GCStack = NULL; - longjmp(*handler, 1); -} - -// Wi-Fi - -/* - (with-client (str [address port]) form*) - Evaluates the forms with str bound to a wifi-stream. -*/ -object *sp_withclient (object *args, object *env) { - object *params = checkarguments(args, 1, 3); - object *var = first(params); - char buffer[BUFFERSIZE]; - params = cdr(params); - int n; - if (params == NULL) { - client = server.available(); - if (!client) return nil; - n = 2; - } else { - object *address = eval(first(params), env); - object *port = eval(second(params), env); - int success; - if (stringp(address)) success = client.connect(cstring(address, buffer, BUFFERSIZE), checkinteger(port)); - else if (integerp(address)) success = client.connect(address->integer, checkinteger(port)); - else error2("invalid address"); - if (!success) return nil; - n = 1; - } - object *pair = cons(var, stream(WIFISTREAM, n)); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - client.stop(); - return result; -} - -/* - (available stream) - Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available. -*/ -object *fn_available (object *args, object *env) { - (void) env; - if (isstream(first(args))>>8 != WIFISTREAM) error2("invalid stream"); - return number(client.available()); -} - -/* - (wifi-server) - Starts a Wi-Fi server running. It returns nil. -*/ -object *fn_wifiserver (object *args, object *env) { - (void) args, (void) env; - server.begin(); - return nil; -} - -/* - (wifi-softap ssid [password channel hidden]) - Set up a soft access point to establish a Wi-Fi network. - Returns the IP address as a string or nil if unsuccessful. -*/ -object *fn_wifisoftap (object *args, object *env) { - (void) env; - char ssid[33], pass[65]; - if (args == NULL) return WiFi.softAPdisconnect(true) ? tee : nil; - object *first = first(args); args = cdr(args); - if (args == NULL) WiFi.softAP(cstring(first, ssid, 33)); - else { - object *second = first(args); - args = cdr(args); - int channel = 1; - bool hidden = false; - if (args != NULL) { - channel = checkinteger(first(args)); - args = cdr(args); - if (args != NULL) hidden = (first(args) != nil); - } - WiFi.softAP(cstring(first, ssid, 33), cstring(second, pass, 65), channel, hidden); - } - return iptostring(WiFi.softAPIP()); -} - -/* - (connected stream) - Returns t or nil to indicate if the client on stream is connected. -*/ -object *fn_connected (object *args, object *env) { - (void) env; - if (isstream(first(args))>>8 != WIFISTREAM) error2("invalid stream"); - return client.connected() ? tee : nil; -} - -/* - (wifi-localip) - Returns the IP address of the local network as a string. -*/ -object *fn_wifilocalip (object *args, object *env) { - (void) args, (void) env; - return iptostring(WiFi.localIP()); -} - -/* - (wifi-connect [ssid pass]) - Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string. -*/ -object *fn_wificonnect (object *args, object *env) { - (void) env; - char ssid[33], pass[65]; - if (args == NULL) { WiFi.disconnect(true); return nil; } - if (cdr(args) == NULL) WiFi.begin(cstring(first(args), ssid, 33)); - else WiFi.begin(cstring(first(args), ssid, 33), cstring(second(args), pass, 65)); - int result = WiFi.waitForConnectResult(); - if (result == WL_CONNECTED) return iptostring(WiFi.localIP()); - else if (result == WL_NO_SSID_AVAIL) error2("network not found"); - else if (result == WL_CONNECT_FAILED) error2("connection failed"); - else error2("unable to connect"); - return nil; -} - -// Graphics functions - -/* - (with-gfx (str) form*) - Evaluates the forms with str bound to an gfx-stream so you can print text - to the graphics display using the standard uLisp print commands. -*/ -object *sp_withgfx (object *args, object *env) { -#if defined(gfxsupport) - object *params = checkarguments(args, 1, 1); - object *var = first(params); - object *pair = cons(var, stream(GFXSTREAM, 1)); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - return result; -#else - (void) args, (void) env; - error2("not supported"); - return nil; -#endif -} - -/* - (draw-pixel x y [colour]) - Draws a pixel at coordinates (x,y) in colour, or white if omitted. -*/ -object *fn_drawpixel (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t colour = COLOR_WHITE; - if (cddr(args) != NULL) colour = checkinteger(third(args)); - tft.drawPixel(checkinteger(first(args)), checkinteger(second(args)), colour); - #else - (void) args; - #endif - return nil; -} - -/* - (draw-line x0 y0 x1 y1 [colour]) - Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted. -*/ -object *fn_drawline (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[4], colour = COLOR_WHITE; - for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawLine(params[0], params[1], params[2], params[3], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (draw-rect x y w h [colour]) - Draws an outline rectangle with its top left corner at (x,y), with width w, - and with height h. The outline is drawn in colour, or white if omitted. -*/ -object *fn_drawrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[4], colour = COLOR_WHITE; - for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawRect(params[0], params[1], params[2], params[3], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (fill-rect x y w h [colour]) - Draws a filled rectangle with its top left corner at (x,y), with width w, - and with height h. The outline is drawn in colour, or white if omitted. -*/ -object *fn_fillrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[4], colour = COLOR_WHITE; - for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillRect(params[0], params[1], params[2], params[3], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (draw-circle x y r [colour]) - Draws an outline circle with its centre at (x, y) and with radius r. - The circle is drawn in colour, or white if omitted. -*/ -object *fn_drawcircle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[3], colour = COLOR_WHITE; - for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawCircle(params[0], params[1], params[2], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (fill-circle x y r [colour]) - Draws a filled circle with its centre at (x, y) and with radius r. - The circle is drawn in colour, or white if omitted. -*/ -object *fn_fillcircle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[3], colour = COLOR_WHITE; - for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillCircle(params[0], params[1], params[2], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (draw-round-rect x y w h radius [colour]) - Draws an outline rounded rectangle with its top left corner at (x,y), with width w, - height h, and corner radius radius. The outline is drawn in colour, or white if omitted. -*/ -object *fn_drawroundrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[5], colour = COLOR_WHITE; - for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawRoundRect(params[0], params[1], params[2], params[3], params[4], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (fill-round-rect x y w h radius [colour]) - Draws a filled rounded rectangle with its top left corner at (x,y), with width w, - height h, and corner radius radius. The outline is drawn in colour, or white if omitted. -*/ -object *fn_fillroundrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[5], colour = COLOR_WHITE; - for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillRoundRect(params[0], params[1], params[2], params[3], params[4], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (draw-triangle x0 y0 x1 y1 x2 y2 [colour]) - Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3). - The outline is drawn in colour, or white if omitted. -*/ -object *fn_drawtriangle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[6], colour = COLOR_WHITE; - for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (fill-triangle x0 y0 x1 y1 x2 y2 [colour]) - Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3). - The outline is drawn in colour, or white if omitted. -*/ -object *fn_filltriangle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[6], colour = COLOR_WHITE; - for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); - #else - (void) args; - #endif - return nil; -} - -/* - (draw-char x y char [colour background size]) - Draws the character char with its top left corner at (x,y). - The character is drawn in a 5 x 7 pixel font in colour against background, - which default to white and black respectively. - The character can optionally be scaled by size. -*/ -object *fn_drawchar (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t colour = COLOR_WHITE, bg = COLOR_BLACK, size = 1; - object *more = cdr(cddr(args)); - if (more != NULL) { - colour = checkinteger(car(more)); - more = cdr(more); - if (more != NULL) { - bg = checkinteger(car(more)); - more = cdr(more); - if (more != NULL) size = checkinteger(car(more)); - } - } - tft.drawChar(checkinteger(first(args)), checkinteger(second(args)), checkchar(third(args)), - colour, bg, size); - #else - (void) args; - #endif - return nil; -} - -/* - (set-cursor x y) - Sets the start point for text plotting to (x, y). -*/ -object *fn_setcursor (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setCursor(checkinteger(first(args)), checkinteger(second(args))); - #else - (void) args; - #endif - return nil; -} - -/* - (set-text-color colour [background]) - Sets the text colour for text plotted using (with-gfx ...). -*/ -object *fn_settextcolor (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - if (cdr(args) != NULL) tft.setTextColor(checkinteger(first(args)), checkinteger(second(args))); - else tft.setTextColor(checkinteger(first(args))); - #else - (void) args; - #endif - return nil; -} - -/* - (set-text-size scale) - Scales text by the specified size, default 1. -*/ -object *fn_settextsize (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setTextSize(checkinteger(first(args))); - #else - (void) args; - #endif - return nil; -} - -/* - (set-text-wrap boolean) - Specified whether text wraps at the right-hand edge of the display; the default is t. -*/ -object *fn_settextwrap (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setTextWrap(first(args) != NULL); - #else - (void) args; - #endif - return nil; -} - -/* - (fill-screen [colour]) - Fills or clears the screen with colour, default black. -*/ -object *fn_fillscreen (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t colour = COLOR_BLACK; - if (args != NULL) colour = checkinteger(first(args)); - tft.fillScreen(colour); - #else - (void) args; - #endif - return nil; -} - -/* - (set-rotation option) - Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3. -*/ -object *fn_setrotation (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setRotation(checkinteger(first(args))); - #else - (void) args; - #endif - return nil; -} - -/* - (invert-display boolean) - Mirror-images the display. -*/ -object *fn_invertdisplay (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.invertDisplay(first(args) != NULL); - #else - (void) args; - #endif - return nil; -} - -// Built-in symbol names -const char string0[] PROGMEM = "nil"; -const char string1[] PROGMEM = "t"; -const char string2[] PROGMEM = "nothing"; -const char string3[] PROGMEM = "&optional"; -const char string4[] PROGMEM = "*features*"; -const char string5[] PROGMEM = ":initial-element"; -const char string6[] PROGMEM = ":element-type"; -const char string7[] PROGMEM = ":test"; -const char string8[] PROGMEM = "bit"; -const char string9[] PROGMEM = "&rest"; -const char string10[] PROGMEM = "lambda"; -const char string11[] PROGMEM = "let"; -const char string12[] PROGMEM = "let*"; -const char string13[] PROGMEM = "closure"; -const char string14[] PROGMEM = "*pc*"; -const char string15[] PROGMEM = "quote"; -const char string16[] PROGMEM = "defun"; -const char string17[] PROGMEM = "defvar"; -const char string18[] PROGMEM = "eq"; -const char string19[] PROGMEM = "car"; -const char string20[] PROGMEM = "first"; -const char string21[] PROGMEM = "cdr"; -const char string22[] PROGMEM = "rest"; -const char string23[] PROGMEM = "nth"; -const char string24[] PROGMEM = "aref"; -const char string25[] PROGMEM = "char"; -const char string26[] PROGMEM = "string"; -const char string27[] PROGMEM = "pinmode"; -const char string28[] PROGMEM = "digitalwrite"; -const char string29[] PROGMEM = "analogread"; -const char string30[] PROGMEM = "register"; -const char string31[] PROGMEM = "format"; -const char string32[] PROGMEM = "or"; -const char string33[] PROGMEM = "setq"; -const char string34[] PROGMEM = "loop"; -const char string35[] PROGMEM = "push"; -const char string36[] PROGMEM = "pop"; -const char string37[] PROGMEM = "incf"; -const char string38[] PROGMEM = "decf"; -const char string39[] PROGMEM = "setf"; -const char string40[] PROGMEM = "dolist"; -const char string41[] PROGMEM = "dotimes"; -const char string42[] PROGMEM = "do"; -const char string43[] PROGMEM = "do*"; -const char string44[] PROGMEM = "trace"; -const char string45[] PROGMEM = "untrace"; -const char string46[] PROGMEM = "for-millis"; -const char string47[] PROGMEM = "time"; -const char string48[] PROGMEM = "with-output-to-string"; -const char string49[] PROGMEM = "with-serial"; -const char string50[] PROGMEM = "with-i2c"; -const char string51[] PROGMEM = "with-sd-card"; -const char string52[] PROGMEM = "progn"; -const char string53[] PROGMEM = "if"; -const char string54[] PROGMEM = "cond"; -const char string55[] PROGMEM = "when"; -const char string56[] PROGMEM = "unless"; -const char string57[] PROGMEM = "case"; -const char string58[] PROGMEM = "and"; -const char string59[] PROGMEM = "not"; -const char string60[] PROGMEM = "null"; -const char string61[] PROGMEM = "cons"; -const char string62[] PROGMEM = "atom"; -const char string63[] PROGMEM = "listp"; -const char string64[] PROGMEM = "consp"; -const char string65[] PROGMEM = "symbolp"; -const char string66[] PROGMEM = "arrayp"; -const char string67[] PROGMEM = "boundp"; -const char string68[] PROGMEM = "keywordp"; -const char string69[] PROGMEM = "set"; -const char string70[] PROGMEM = "streamp"; -const char string71[] PROGMEM = "equal"; -const char string72[] PROGMEM = "caar"; -const char string73[] PROGMEM = "cadr"; -const char string74[] PROGMEM = "second"; -const char string75[] PROGMEM = "cdar"; -const char string76[] PROGMEM = "cddr"; -const char string77[] PROGMEM = "caaar"; -const char string78[] PROGMEM = "caadr"; -const char string79[] PROGMEM = "cadar"; -const char string80[] PROGMEM = "caddr"; -const char string81[] PROGMEM = "third"; -const char string82[] PROGMEM = "cdaar"; -const char string83[] PROGMEM = "cdadr"; -const char string84[] PROGMEM = "cddar"; -const char string85[] PROGMEM = "cdddr"; -const char string86[] PROGMEM = "length"; -const char string87[] PROGMEM = "array-dimensions"; -const char string88[] PROGMEM = "list"; -const char string89[] PROGMEM = "copy-list"; -const char string90[] PROGMEM = "make-array"; -const char string91[] PROGMEM = "reverse"; -const char string92[] PROGMEM = "assoc"; -const char string93[] PROGMEM = "member"; -const char string94[] PROGMEM = "apply"; -const char string95[] PROGMEM = "funcall"; -const char string96[] PROGMEM = "append"; -const char string97[] PROGMEM = "mapc"; -const char string98[] PROGMEM = "mapl"; -const char string99[] PROGMEM = "mapcar"; -const char string100[] PROGMEM = "mapcan"; -const char string101[] PROGMEM = "maplist"; -const char string102[] PROGMEM = "mapcon"; -const char string103[] PROGMEM = "+"; -const char string104[] PROGMEM = "-"; -const char string105[] PROGMEM = "*"; -const char string106[] PROGMEM = "/"; -const char string107[] PROGMEM = "mod"; -const char string108[] PROGMEM = "1+"; -const char string109[] PROGMEM = "1-"; -const char string110[] PROGMEM = "abs"; -const char string111[] PROGMEM = "random"; -const char string112[] PROGMEM = "max"; -const char string113[] PROGMEM = "min"; -const char string114[] PROGMEM = "/="; -const char string115[] PROGMEM = "="; -const char string116[] PROGMEM = "<"; -const char string117[] PROGMEM = "<="; -const char string118[] PROGMEM = ">"; -const char string119[] PROGMEM = ">="; -const char string120[] PROGMEM = "plusp"; -const char string121[] PROGMEM = "minusp"; -const char string122[] PROGMEM = "zerop"; -const char string123[] PROGMEM = "oddp"; -const char string124[] PROGMEM = "evenp"; -const char string125[] PROGMEM = "integerp"; -const char string126[] PROGMEM = "numberp"; -const char string127[] PROGMEM = "float"; -const char string128[] PROGMEM = "floatp"; -const char string129[] PROGMEM = "sin"; -const char string130[] PROGMEM = "cos"; -const char string131[] PROGMEM = "tan"; -const char string132[] PROGMEM = "asin"; -const char string133[] PROGMEM = "acos"; -const char string134[] PROGMEM = "atan"; -const char string135[] PROGMEM = "sinh"; -const char string136[] PROGMEM = "cosh"; -const char string137[] PROGMEM = "tanh"; -const char string138[] PROGMEM = "exp"; -const char string139[] PROGMEM = "sqrt"; -const char string140[] PROGMEM = "log"; -const char string141[] PROGMEM = "expt"; -const char string142[] PROGMEM = "ceiling"; -const char string143[] PROGMEM = "floor"; -const char string144[] PROGMEM = "truncate"; -const char string145[] PROGMEM = "round"; -const char string146[] PROGMEM = "char-code"; -const char string147[] PROGMEM = "code-char"; -const char string148[] PROGMEM = "characterp"; -const char string149[] PROGMEM = "stringp"; -const char string150[] PROGMEM = "string="; -const char string151[] PROGMEM = "string<"; -const char string152[] PROGMEM = "string>"; -const char string153[] PROGMEM = "string/="; -const char string154[] PROGMEM = "string<="; -const char string155[] PROGMEM = "string>="; -const char string156[] PROGMEM = "sort"; -const char string157[] PROGMEM = "concatenate"; -const char string158[] PROGMEM = "subseq"; -const char string159[] PROGMEM = "search"; -const char string160[] PROGMEM = "read-from-string"; -const char string161[] PROGMEM = "princ-to-string"; -const char string162[] PROGMEM = "prin1-to-string"; -const char string163[] PROGMEM = "logand"; -const char string164[] PROGMEM = "logior"; -const char string165[] PROGMEM = "logxor"; -const char string166[] PROGMEM = "lognot"; -const char string167[] PROGMEM = "ash"; -const char string168[] PROGMEM = "logbitp"; -const char string169[] PROGMEM = "eval"; -const char string170[] PROGMEM = "return"; -const char string171[] PROGMEM = "globals"; -const char string172[] PROGMEM = "locals"; -const char string173[] PROGMEM = "makunbound"; -const char string174[] PROGMEM = "break"; -const char string175[] PROGMEM = "read"; -const char string176[] PROGMEM = "prin1"; -const char string177[] PROGMEM = "print"; -const char string178[] PROGMEM = "princ"; -const char string179[] PROGMEM = "terpri"; -const char string180[] PROGMEM = "read-byte"; -const char string181[] PROGMEM = "read-line"; -const char string182[] PROGMEM = "write-byte"; -const char string183[] PROGMEM = "write-string"; -const char string184[] PROGMEM = "write-line"; -const char string185[] PROGMEM = "restart-i2c"; -const char string186[] PROGMEM = "gc"; -const char string187[] PROGMEM = "room"; -const char string188[] PROGMEM = "save-image"; -const char string189[] PROGMEM = "load-image"; -const char string190[] PROGMEM = "cls"; -const char string191[] PROGMEM = "digitalread"; -const char string192[] PROGMEM = "analogreadresolution"; -const char string193[] PROGMEM = "analogwrite"; -const char string194[] PROGMEM = "delay"; -const char string195[] PROGMEM = "millis"; -const char string196[] PROGMEM = "sleep"; -const char string197[] PROGMEM = "note"; -const char string198[] PROGMEM = "edit"; -const char string199[] PROGMEM = "pprint"; -const char string200[] PROGMEM = "pprintall"; -const char string201[] PROGMEM = "require"; -const char string202[] PROGMEM = "list-library"; -const char string203[] PROGMEM = "?"; -const char string204[] PROGMEM = "documentation"; -const char string205[] PROGMEM = "apropos"; -const char string206[] PROGMEM = "apropos-list"; -const char string207[] PROGMEM = "unwind-protect"; -const char string208[] PROGMEM = "ignore-errors"; -const char string209[] PROGMEM = "error"; -const char string210[] PROGMEM = "with-client"; -const char string211[] PROGMEM = "available"; -const char string212[] PROGMEM = "wifi-server"; -const char string213[] PROGMEM = "wifi-softap"; -const char string214[] PROGMEM = "connected"; -const char string215[] PROGMEM = "wifi-localip"; -const char string216[] PROGMEM = "wifi-connect"; -const char string217[] PROGMEM = "with-gfx"; -const char string218[] PROGMEM = "draw-pixel"; -const char string219[] PROGMEM = "draw-line"; -const char string220[] PROGMEM = "draw-rect"; -const char string221[] PROGMEM = "fill-rect"; -const char string222[] PROGMEM = "draw-circle"; -const char string223[] PROGMEM = "fill-circle"; -const char string224[] PROGMEM = "draw-round-rect"; -const char string225[] PROGMEM = "fill-round-rect"; -const char string226[] PROGMEM = "draw-triangle"; -const char string227[] PROGMEM = "fill-triangle"; -const char string228[] PROGMEM = "draw-char"; -const char string229[] PROGMEM = "set-cursor"; -const char string230[] PROGMEM = "set-text-color"; -const char string231[] PROGMEM = "set-text-size"; -const char string232[] PROGMEM = "set-text-wrap"; -const char string233[] PROGMEM = "fill-screen"; -const char string234[] PROGMEM = "set-rotation"; -const char string235[] PROGMEM = "invert-display"; -const char string236[] PROGMEM = ":led-builtin"; -const char string237[] PROGMEM = ":high"; -const char string238[] PROGMEM = ":low"; -const char string239[] PROGMEM = ":input"; -const char string240[] PROGMEM = ":input-pullup"; -const char string241[] PROGMEM = ":input-pulldown"; -const char string242[] PROGMEM = ":output"; - -// Documentation strings -const char doc0[] PROGMEM = "nil\n" -"A symbol equivalent to the empty list (). Also represents false."; -const char doc1[] PROGMEM = "t\n" -"A symbol representing true."; -const char doc2[] PROGMEM = "nothing\n" -"A symbol with no value.\n" -"It is useful if you want to suppress printing the result of evaluating a function."; -const char doc3[] PROGMEM = "&optional\n" -"Can be followed by one or more optional parameters in a lambda or defun parameter list."; -const char doc4[] PROGMEM = "*features*\n" -"Returns a list of keywords representing features supported by this platform."; -const char doc9[] PROGMEM = "&rest\n" -"Can be followed by a parameter in a lambda or defun parameter list,\n" -"and is assigned a list of the corresponding arguments."; -const char doc10[] PROGMEM = "(lambda (parameter*) form*)\n" -"Creates an unnamed function with parameters. The body is evaluated with the parameters as local variables\n" -"whose initial values are defined by the values of the forms after the lambda form."; -const char doc11[] PROGMEM = "(let ((var value) ... ) forms*)\n" -"Declares local variables with values, and evaluates the forms with those local variables."; -const char doc12[] PROGMEM = "(let* ((var value) ... ) forms*)\n" -"Declares local variables with values, and evaluates the forms with those local variables.\n" -"Each declaration can refer to local variables that have been defined earlier in the let*."; -const char doc16[] PROGMEM = "(defun name (parameters) form*)\n" -"Defines a function."; -const char doc17[] PROGMEM = "(defvar variable form)\n" -"Defines a global variable."; -const char doc18[] PROGMEM = "(eq item item)\n" -"Tests whether the two arguments are the same symbol, same character, equal numbers,\n" -"or point to the same cons, and returns t or nil as appropriate."; -const char doc19[] PROGMEM = "(car list)\n" -"Returns the first item in a list."; -const char doc21[] PROGMEM = "(cdr list)\n" -"Returns a list with the first item removed."; -const char doc23[] PROGMEM = "(nth number list)\n" -"Returns the nth item in list, counting from zero."; -const char doc24[] PROGMEM = "(aref array index [index*])\n" -"Returns an element from the specified array."; -const char doc25[] PROGMEM = "(char string n)\n" -"Returns the nth character in a string, counting from zero."; -const char doc26[] PROGMEM = "(string item)\n" -"Converts its argument to a string."; -const char doc27[] PROGMEM = "(pinmode pin mode)\n" -"Sets the input/output mode of an Arduino pin number, and returns nil.\n" -"The mode parameter can be an integer, a keyword, or t or nil."; -const char doc28[] PROGMEM = "(digitalwrite pin state)\n" -"Sets the state of the specified Arduino pin number."; -const char doc29[] PROGMEM = "(analogread pin)\n" -"Reads the specified Arduino analogue pin number and returns the value."; -const char doc30[] PROGMEM = "(register address [value])\n" -"Reads or writes the value of a peripheral register.\n" -"If value is not specified the function returns the value of the register at address.\n" -"If value is specified the value is written to the register at address and the function returns value."; -const char doc31[] PROGMEM = "(format output controlstring [arguments]*)\n" -"Outputs its arguments formatted according to the format directives in controlstring."; -const char doc32[] PROGMEM = "(or item*)\n" -"Evaluates its arguments until one returns non-nil, and returns its value."; -const char doc33[] PROGMEM = "(setq symbol value [symbol value]*)\n" -"For each pair of arguments assigns the value of the second argument\n" -"to the variable specified in the first argument."; -const char doc34[] PROGMEM = "(loop forms*)\n" -"Executes its arguments repeatedly until one of the arguments calls (return),\n" -"which then causes an exit from the loop."; -const char doc35[] PROGMEM = "(push item place)\n" -"Modifies the value of place, which should be a list, to add item onto the front of the list,\n" -"and returns the new list."; -const char doc36[] PROGMEM = "(pop place)\n" -"Modifies the value of place, which should be a non-nil list, to remove its first item,\n" -"and returns that item."; -const char doc37[] PROGMEM = "(incf place [number])\n" -"Increments a place, which should have an numeric value, and returns the result.\n" -"The third argument is an optional increment which defaults to 1."; -const char doc38[] PROGMEM = "(decf place [number])\n" -"Decrements a place, which should have an numeric value, and returns the result.\n" -"The third argument is an optional decrement which defaults to 1."; -const char doc39[] PROGMEM = "(setf place value [place value]*)\n" -"For each pair of arguments modifies a place to the result of evaluating value."; -const char doc40[] PROGMEM = "(dolist (var list [result]) form*)\n" -"Sets the local variable var to each element of list in turn, and executes the forms.\n" -"It then returns result, or nil if result is omitted."; -const char doc41[] PROGMEM = "(dotimes (var number [result]) form*)\n" -"Executes the forms number times, with the local variable var set to each integer from 0 to number-1 in turn.\n" -"It then returns result, or nil if result is omitted."; -const char doc42[] PROGMEM = "(do ((var [init [step]])*) (end-test result*) form*)\n" -"Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step sequentially.\n" -"The forms are executed until end-test is true. It returns result."; -const char doc43[] PROGMEM = "(do* ((var [init [step]])*) (end-test result*) form*)\n" -"Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step in parallel.\n" -"The forms are executed until end-test is true. It returns result."; -const char doc44[] PROGMEM = "(trace [function]*)\n" -"Turns on tracing of up to TRACEMAX user-defined functions,\n" -"and returns a list of the functions currently being traced."; -const char doc45[] PROGMEM = "(untrace [function]*)\n" -"Turns off tracing of up to TRACEMAX user-defined functions, and returns a list of the functions untraced.\n" -"If no functions are specified it untraces all functions."; -const char doc46[] PROGMEM = "(for-millis ([number]) form*)\n" -"Executes the forms and then waits until a total of number milliseconds have elapsed.\n" -"Returns the total number of milliseconds taken."; -const char doc47[] PROGMEM = "(time form)\n" -"Prints the value returned by the form, and the time taken to evaluate the form\n" -"in milliseconds or seconds."; -const char doc48[] PROGMEM = "(with-output-to-string (str) form*)\n" -"Returns a string containing the output to the stream variable str."; -const char doc49[] PROGMEM = "(with-serial (str port [baud]) form*)\n" -"Evaluates the forms with str bound to a serial-stream using port.\n" -"The optional baud gives the baud rate divided by 100, default 96."; -const char doc50[] PROGMEM = "(with-i2c (str [port] address [read-p]) form*)\n" -"Evaluates the forms with str bound to an i2c-stream defined by address.\n" -"If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes\n" -"to be read from the stream. If port is omitted it defaults to 0, otherwise it specifies the port, 0 or 1."; -const char doc51[] PROGMEM = "(with-sd-card (str filename [mode]) form*)\n" -"Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename.\n" -"If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite."; -const char doc52[] PROGMEM = "(progn form*)\n" -"Evaluates several forms grouped together into a block, and returns the result of evaluating the last form."; -const char doc53[] PROGMEM = "(if test then [else])\n" -"Evaluates test. If it's non-nil the form then is evaluated and returned;\n" -"otherwise the form else is evaluated and returned."; -const char doc54[] PROGMEM = "(cond ((test form*) (test form*) ... ))\n" -"Each argument is a list consisting of a test optionally followed by one or more forms.\n" -"If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond.\n" -"If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way."; -const char doc55[] PROGMEM = "(when test form*)\n" -"Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned."; -const char doc56[] PROGMEM = "(unless test form*)\n" -"Evaluates the test. If it's nil the forms are evaluated and the last value is returned."; -const char doc57[] PROGMEM = "(case keyform ((key form*) (key form*) ... ))\n" -"Evaluates a keyform to produce a test key, and then tests this against a series of arguments,\n" -"each of which is a list containing a key optionally followed by one or more forms."; -const char doc58[] PROGMEM = "(and item*)\n" -"Evaluates its arguments until one returns nil, and returns the last value."; -const char doc59[] PROGMEM = "(not item)\n" -"Returns t if its argument is nil, or nil otherwise. Equivalent to null."; -const char doc61[] PROGMEM = "(cons item item)\n" -"If the second argument is a list, cons returns a new list with item added to the front of the list.\n" -"If the second argument isn't a list cons returns a dotted pair."; -const char doc62[] PROGMEM = "(atom item)\n" -"Returns t if its argument is a single number, symbol, or nil."; -const char doc63[] PROGMEM = "(listp item)\n" -"Returns t if its argument is a list."; -const char doc64[] PROGMEM = "(consp item)\n" -"Returns t if its argument is a non-null list."; -const char doc65[] PROGMEM = "(symbolp item)\n" -"Returns t if its argument is a symbol."; -const char doc66[] PROGMEM = "(arrayp item)\n" -"Returns t if its argument is an array."; -const char doc67[] PROGMEM = "(boundp item)\n" -"Returns t if its argument is a symbol with a value."; -const char doc68[] PROGMEM = "(keywordp item)\n" -"Returns t if its argument is a built-in or user-defined keyword."; -const char doc69[] PROGMEM = "(set symbol value [symbol value]*)\n" -"For each pair of arguments, assigns the value of the second argument to the value of the first argument."; -const char doc70[] PROGMEM = "(streamp item)\n" -"Returns t if its argument is a stream."; -const char doc71[] PROGMEM = "(equal item item)\n" -"Tests whether the two arguments are the same symbol, same character, equal numbers,\n" -"or point to the same cons, and returns t or nil as appropriate."; -const char doc72[] PROGMEM = "(caar list)"; -const char doc73[] PROGMEM = "(cadr list)"; -const char doc75[] PROGMEM = "(cdar list)\n" -"Equivalent to (cdr (car list))."; -const char doc76[] PROGMEM = "(cddr list)\n" -"Equivalent to (cdr (cdr list))."; -const char doc77[] PROGMEM = "(caaar list)\n" -"Equivalent to (car (car (car list)))."; -const char doc78[] PROGMEM = "(caadr list)\n" -"Equivalent to (car (car (cdar list)))."; -const char doc79[] PROGMEM = "(cadar list)\n" -"Equivalent to (car (cdr (car list)))."; -const char doc80[] PROGMEM = "(caddr list)\n" -"Equivalent to (car (cdr (cdr list)))."; -const char doc82[] PROGMEM = "(cdaar list)\n" -"Equivalent to (cdar (car (car list)))."; -const char doc83[] PROGMEM = "(cdadr list)\n" -"Equivalent to (cdr (car (cdr list)))."; -const char doc84[] PROGMEM = "(cddar list)\n" -"Equivalent to (cdr (cdr (car list)))."; -const char doc85[] PROGMEM = "(cdddr list)\n" -"Equivalent to (cdr (cdr (cdr list)))."; -const char doc86[] PROGMEM = "(length item)\n" -"Returns the number of items in a list, the length of a string, or the length of a one-dimensional array."; -const char doc87[] PROGMEM = "(array-dimensions item)\n" -"Returns a list of the dimensions of an array."; -const char doc88[] PROGMEM = "(list item*)\n" -"Returns a list of the values of its arguments."; -const char doc89[] PROGMEM = "(copy-list list)\n" -"Returns a copy of a list."; -const char doc90[] PROGMEM = "(make-array size [:initial-element element] [:element-type 'bit])\n" -"If size is an integer it creates a one-dimensional array with elements from 0 to size-1.\n" -"If size is a list of n integers it creates an n-dimensional array with those dimensions.\n" -"If :element-type 'bit is specified the array is a bit array."; -const char doc91[] PROGMEM = "(reverse list)\n" -"Returns a list with the elements of list in reverse order."; -const char doc92[] PROGMEM = "(assoc key list [:test function])\n" -"Looks up a key in an association list of (key . value) pairs, using eq or the specified test function,\n" -"and returns the matching pair, or nil if no pair is found."; -const char doc93[] PROGMEM = "(member item list [:test function])\n" -"Searches for an item in a list, using eq or the specified test function, and returns the list starting\n" -"from the first occurrence of the item, or nil if it is not found."; -const char doc94[] PROGMEM = "(apply function list)\n" -"Returns the result of evaluating function, with the list of arguments specified by the second parameter."; -const char doc95[] PROGMEM = "(funcall function argument*)\n" -"Evaluates function with the specified arguments."; -const char doc96[] PROGMEM = "(append list*)\n" -"Joins its arguments, which should be lists, into a single list."; -const char doc97[] PROGMEM = "(mapc function list1 [list]*)\n" -"Applies the function to each element in one or more lists, ignoring the results.\n" -"It returns the first list argument."; -const char doc98[] PROGMEM = "(mapl function list1 [list]*)\n" -"Applies the function to one or more lists and then successive cdrs of those lists,\n" -"ignoring the results. It returns the first list argument."; -const char doc99[] PROGMEM = "(mapcar function list1 [list]*)\n" -"Applies the function to each element in one or more lists, and returns the resulting list."; -const char doc100[] PROGMEM = "(mapcan function list1 [list]*)\n" -"Applies the function to each element in one or more lists. The results should be lists,\n" -"and these are destructively concatenated together to give the value returned."; -const char doc101[] PROGMEM = "(maplist function list1 [list]*)\n" -"Applies the function to one or more lists and then successive cdrs of those lists,\n" -"and returns the resulting list."; -const char doc102[] PROGMEM = "(mapcon function list1 [list]*)\n" -"Applies the function to one or more lists and then successive cdrs of those lists,\n" -"and these are destructively concatenated together to give the value returned."; -const char doc103[] PROGMEM = "(+ number*)\n" -"Adds its arguments together.\n" -"If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n" -"otherwise a floating-point number."; -const char doc104[] PROGMEM = "(- number*)\n" -"If there is one argument, negates the argument.\n" -"If there are two or more arguments, subtracts the second and subsequent arguments from the first argument.\n" -"If each argument is an integer, and the running total doesn't overflow, returns the result as an integer,\n" -"otherwise a floating-point number."; -const char doc105[] PROGMEM = "(* number*)\n" -"Multiplies its arguments together.\n" -"If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n" -"otherwise it's a floating-point number."; -const char doc106[] PROGMEM = "(/ number*)\n" -"Divides the first argument by the second and subsequent arguments.\n" -"If each argument is an integer, and each division produces an exact result, the result is an integer;\n" -"otherwise it's a floating-point number."; -const char doc107[] PROGMEM = "(mod number number)\n" -"Returns its first argument modulo the second argument.\n" -"If both arguments are integers the result is an integer; otherwise it's a floating-point number."; -const char doc108[] PROGMEM = "(1+ number)\n" -"Adds one to its argument and returns it.\n" -"If the argument is an integer the result is an integer if possible;\n" -"otherwise it's a floating-point number."; -const char doc109[] PROGMEM = "(1- number)\n" -"Subtracts one from its argument and returns it.\n" -"If the argument is an integer the result is an integer if possible;\n" -"otherwise it's a floating-point number."; -const char doc110[] PROGMEM = "(abs number)\n" -"Returns the absolute, positive value of its argument.\n" -"If the argument is an integer the result will be returned as an integer if possible,\n" -"otherwise a floating-point number."; -const char doc111[] PROGMEM = "(random number)\n" -"If number is an integer returns a random number between 0 and one less than its argument.\n" -"Otherwise returns a floating-point number between zero and number."; -const char doc112[] PROGMEM = "(max number*)\n" -"Returns the maximum of one or more arguments."; -const char doc113[] PROGMEM = "(min number*)\n" -"Returns the minimum of one or more arguments."; -const char doc114[] PROGMEM = "(/= number*)\n" -"Returns t if none of the arguments are equal, or nil if two or more arguments are equal."; -const char doc115[] PROGMEM = "(= number*)\n" -"Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise."; -const char doc116[] PROGMEM = "(< number*)\n" -"Returns t if each argument is less than the next argument, and nil otherwise."; -const char doc117[] PROGMEM = "(<= number*)\n" -"Returns t if each argument is less than or equal to the next argument, and nil otherwise."; -const char doc118[] PROGMEM = "(> number*)\n" -"Returns t if each argument is greater than the next argument, and nil otherwise."; -const char doc119[] PROGMEM = "(>= number*)\n" -"Returns t if each argument is greater than or equal to the next argument, and nil otherwise."; -const char doc120[] PROGMEM = "(plusp number)\n" -"Returns t if the argument is greater than zero, or nil otherwise."; -const char doc121[] PROGMEM = "(minusp number)\n" -"Returns t if the argument is less than zero, or nil otherwise."; -const char doc122[] PROGMEM = "(zerop number)\n" -"Returns t if the argument is zero."; -const char doc123[] PROGMEM = "(oddp number)\n" -"Returns t if the integer argument is odd."; -const char doc124[] PROGMEM = "(evenp number)\n" -"Returns t if the integer argument is even."; -const char doc125[] PROGMEM = "(integerp number)\n" -"Returns t if the argument is an integer."; -const char doc126[] PROGMEM = "(numberp number)\n" -"Returns t if the argument is a number."; -const char doc127[] PROGMEM = "(float number)\n" -"Returns its argument converted to a floating-point number."; -const char doc128[] PROGMEM = "(floatp number)\n" -"Returns t if the argument is a floating-point number."; -const char doc129[] PROGMEM = "(sin number)\n" -"Returns sin(number)."; -const char doc130[] PROGMEM = "(cos number)\n" -"Returns cos(number)."; -const char doc131[] PROGMEM = "(tan number)\n" -"Returns tan(number)."; -const char doc132[] PROGMEM = "(asin number)\n" -"Returns asin(number)."; -const char doc133[] PROGMEM = "(acos number)\n" -"Returns acos(number)."; -const char doc134[] PROGMEM = "(atan number1 [number2])\n" -"Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1."; -const char doc135[] PROGMEM = "(sinh number)\n" -"Returns sinh(number)."; -const char doc136[] PROGMEM = "(cosh number)\n" -"Returns cosh(number)."; -const char doc137[] PROGMEM = "(tanh number)\n" -"Returns tanh(number)."; -const char doc138[] PROGMEM = "(exp number)\n" -"Returns exp(number)."; -const char doc139[] PROGMEM = "(sqrt number)\n" -"Returns sqrt(number)."; -const char doc140[] PROGMEM = "(log number [base])\n" -"Returns the logarithm of number to the specified base. If base is omitted it defaults to e."; -const char doc141[] PROGMEM = "(expt number power)\n" -"Returns number raised to the specified power.\n" -"Returns the result as an integer if the arguments are integers and the result will be within range,\n" -"otherwise a floating-point number."; -const char doc142[] PROGMEM = "(ceiling number [divisor])\n" -"Returns ceil(number/divisor). If omitted, divisor is 1."; -const char doc143[] PROGMEM = "(floor number [divisor])\n" -"Returns floor(number/divisor). If omitted, divisor is 1."; -const char doc144[] PROGMEM = "(truncate number [divisor])\n" -"Returns the integer part of number/divisor. If divisor is omitted it defaults to 1."; -const char doc145[] PROGMEM = "(round number [divisor])\n" -"Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1."; -const char doc146[] PROGMEM = "(char-code character)\n" -"Returns the ASCII code for a character, as an integer."; -const char doc147[] PROGMEM = "(code-char integer)\n" -"Returns the character for the specified ASCII code."; -const char doc148[] PROGMEM = "(characterp item)\n" -"Returns t if the argument is a character and nil otherwise."; -const char doc149[] PROGMEM = "(stringp item)\n" -"Returns t if the argument is a string and nil otherwise."; -const char doc150[] PROGMEM = "(string= string string)\n" -"Returns t if the two strings are the same, or nil otherwise."; -const char doc151[] PROGMEM = "(string< string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically less than the second string,\n" -"or nil otherwise."; -const char doc152[] PROGMEM = "(string> string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically greater than the second string,\n" -"or nil otherwise."; -const char doc153[] PROGMEM = "(string/= string string)\n" -"Returns the index to the first mismatch if the two strings are not the same, or nil otherwise."; -const char doc154[] PROGMEM = "(string<= string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically less than or equal to\n" -"the second string, or nil otherwise."; -const char doc155[] PROGMEM = "(string>= string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically greater than or equal to\n" -"the second string, or nil otherwise."; -const char doc156[] PROGMEM = "(sort list test)\n" -"Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list."; -const char doc157[] PROGMEM = "(concatenate 'string string*)\n" -"Joins together the strings given in the second and subsequent arguments, and returns a single string."; -const char doc158[] PROGMEM = "(subseq seq start [end])\n" -"Returns a subsequence of a list or string from item start to item end-1."; -const char doc159[] PROGMEM = "(search pattern target [:test function])\n" -"Returns the index of the first occurrence of pattern in target, or nil if it's not found.\n" -"The target can be a list or string. If it's a list a test function can be specified; default eq."; -const char doc160[] PROGMEM = "(read-from-string string)\n" -"Reads an atom or list from the specified string and returns it."; -const char doc161[] PROGMEM = "(princ-to-string item)\n" -"Prints its argument to a string, and returns the string.\n" -"Characters and strings are printed without quotation marks or escape characters."; -const char doc162[] PROGMEM = "(prin1-to-string item [stream])\n" -"Prints its argument to a string, and returns the string.\n" -"Characters and strings are printed with quotation marks and escape characters,\n" -"in a format that will be suitable for read-from-string."; -const char doc163[] PROGMEM = "(logand [value*])\n" -"Returns the bitwise & of the values."; -const char doc164[] PROGMEM = "(logior [value*])\n" -"Returns the bitwise | of the values."; -const char doc165[] PROGMEM = "(logxor [value*])\n" -"Returns the bitwise ^ of the values."; -const char doc166[] PROGMEM = "(lognot value)\n" -"Returns the bitwise logical NOT of the value."; -const char doc167[] PROGMEM = "(ash value shift)\n" -"Returns the result of bitwise shifting value by shift bits. If shift is positive, value is shifted to the left."; -const char doc168[] PROGMEM = "(logbitp bit value)\n" -"Returns t if bit number bit in value is a '1', and nil if it is a '0'."; -const char doc169[] PROGMEM = "(eval form*)\n" -"Evaluates its argument an extra time."; -const char doc170[] PROGMEM = "(return [value])\n" -"Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value."; -const char doc171[] PROGMEM = "(globals)\n" -"Returns a list of global variables."; -const char doc172[] PROGMEM = "(locals)\n" -"Returns an association list of local variables and their values."; -const char doc173[] PROGMEM = "(makunbound symbol)\n" -"Removes the value of the symbol from GlobalEnv and returns the symbol."; -const char doc174[] PROGMEM = "(break)\n" -"Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL."; -const char doc175[] PROGMEM = "(read [stream])\n" -"Reads an atom or list from the serial input and returns it.\n" -"If stream is specified the item is read from the specified stream."; -const char doc176[] PROGMEM = "(prin1 item [stream])\n" -"Prints its argument, and returns its value.\n" -"Strings are printed with quotation marks and escape characters."; -const char doc177[] PROGMEM = "(print item [stream])\n" -"Prints its argument with quotation marks and escape characters, on a new line, and followed by a space.\n" -"If stream is specified the argument is printed to the specified stream."; -const char doc178[] PROGMEM = "(princ item [stream])\n" -"Prints its argument, and returns its value.\n" -"Characters and strings are printed without quotation marks or escape characters."; -const char doc179[] PROGMEM = "(terpri [stream])\n" -"Prints a new line, and returns nil.\n" -"If stream is specified the new line is written to the specified stream."; -const char doc180[] PROGMEM = "(read-byte stream)\n" -"Reads a byte from a stream and returns it."; -const char doc181[] PROGMEM = "(read-line [stream])\n" -"Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline.\n" -"If stream is specified the line is read from the specified stream."; -const char doc182[] PROGMEM = "(write-byte number [stream])\n" -"Writes a byte to a stream."; -const char doc183[] PROGMEM = "(write-string string [stream])\n" -"Writes a string. If stream is specified the string is written to the stream."; -const char doc184[] PROGMEM = "(write-line string [stream])\n" -"Writes a string terminated by a newline character. If stream is specified the string is written to the stream."; -const char doc185[] PROGMEM = "(restart-i2c stream [read-p])\n" -"Restarts an i2c-stream.\n" -"If read-p is nil or omitted the stream is written to.\n" -"If read-p is an integer it specifies the number of bytes to be read from the stream."; -const char doc186[] PROGMEM = "(gc)\n" -"Forces a garbage collection and prints the number of objects collected, and the time taken."; -const char doc187[] PROGMEM = "(room)\n" -"Returns the number of free Lisp cells remaining."; -const char doc188[] PROGMEM = "(save-image [symbol])\n" -"Saves the current uLisp image to non-volatile memory or SD card so it can be loaded using load-image."; -const char doc189[] PROGMEM = "(load-image [filename])\n" -"Loads a saved uLisp image from non-volatile memory or SD card."; -const char doc190[] PROGMEM = "(cls)\n" -"Prints a clear-screen character."; -const char doc191[] PROGMEM = "(digitalread pin)\n" -"Reads the state of the specified Arduino pin number and returns t (high) or nil (low)."; -const char doc192[] PROGMEM = "(analogreadresolution bits)\n" -"Specifies the resolution for the analogue inputs on platforms that support it.\n" -"The default resolution on all platforms is 10 bits."; -const char doc193[] PROGMEM = "(analogwrite pin value)\n" -"Writes the value to the specified Arduino pin number."; -const char doc194[] PROGMEM = "(delay number)\n" -"Delays for a specified number of milliseconds."; -const char doc195[] PROGMEM = "(millis)\n" -"Returns the time in milliseconds that uLisp has been running."; -const char doc196[] PROGMEM = "(sleep secs)\n" -"Puts the processor into a low-power sleep mode for secs.\n" -"Only supported on some platforms. On other platforms it does delay(1000*secs)."; -const char doc197[] PROGMEM = "(note [pin] [note] [octave])\n" -"Generates a square wave on pin.\n" -"note represents the note in the well-tempered scale.\n" -"The argument octave can specify an octave; default 0."; -const char doc198[] PROGMEM = "(edit 'function)\n" -"Calls the Lisp tree editor to allow you to edit a function definition."; -const char doc199[] PROGMEM = "(pprint item [str])\n" -"Prints its argument, using the pretty printer, to display it formatted in a structured way.\n" -"If str is specified it prints to the specified stream. It returns no value."; -const char doc200[] PROGMEM = "(pprintall [str])\n" -"Pretty-prints the definition of every function and variable defined in the uLisp workspace.\n" -"If str is specified it prints to the specified stream. It returns no value."; -const char doc201[] PROGMEM = "(require 'symbol)\n" -"Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library.\n" -"It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library."; -const char doc202[] PROGMEM = "(list-library)\n" -"Prints a list of the functions defined in the List Library."; -const char doc203[] PROGMEM = "(? item)\n" -"Prints the documentation string of a built-in or user-defined function."; -const char doc204[] PROGMEM = "(documentation 'symbol [type])\n" -"Returns the documentation string of a built-in or user-defined function. The type argument is ignored."; -const char doc205[] PROGMEM = "(apropos item)\n" -"Prints the user-defined and built-in functions whose names contain the specified string or symbol."; -const char doc206[] PROGMEM = "(apropos-list item)\n" -"Returns a list of user-defined and built-in functions whose names contain the specified string or symbol."; -const char doc207[] PROGMEM = "(unwind-protect form1 [forms]*)\n" -"Evaluates form1 and forms in order and returns the value of form1,\n" -"but guarantees to evaluate forms even if an error occurs in form1."; -const char doc208[] PROGMEM = "(ignore-errors [forms]*)\n" -"Evaluates forms ignoring errors."; -const char doc209[] PROGMEM = "(error controlstring [arguments]*)\n" -"Signals an error. The message is printed by format using the controlstring and arguments."; -const char doc210[] PROGMEM = "(with-client (str [address port]) form*)\n" -"Evaluates the forms with str bound to a wifi-stream."; -const char doc211[] PROGMEM = "(available stream)\n" -"Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available."; -const char doc212[] PROGMEM = "(wifi-server)\n" -"Starts a Wi-Fi server running. It returns nil."; -const char doc213[] PROGMEM = "(wifi-softap ssid [password channel hidden])\n" -"Set up a soft access point to establish a Wi-Fi network.\n" -"Returns the IP address as a string or nil if unsuccessful."; -const char doc214[] PROGMEM = "(connected stream)\n" -"Returns t or nil to indicate if the client on stream is connected."; -const char doc215[] PROGMEM = "(wifi-localip)\n" -"Returns the IP address of the local network as a string."; -const char doc216[] PROGMEM = "(wifi-connect [ssid pass])\n" -"Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string."; -const char doc217[] PROGMEM = "(with-gfx (str) form*)\n" -"Evaluates the forms with str bound to an gfx-stream so you can print text\n" -"to the graphics display using the standard uLisp print commands."; -const char doc218[] PROGMEM = "(draw-pixel x y [colour])\n" -"Draws a pixel at coordinates (x,y) in colour, or white if omitted."; -const char doc219[] PROGMEM = "(draw-line x0 y0 x1 y1 [colour])\n" -"Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted."; -const char doc220[] PROGMEM = "(draw-rect x y w h [colour])\n" -"Draws an outline rectangle with its top left corner at (x,y), with width w,\n" -"and with height h. The outline is drawn in colour, or white if omitted."; -const char doc221[] PROGMEM = "(fill-rect x y w h [colour])\n" -"Draws a filled rectangle with its top left corner at (x,y), with width w,\n" -"and with height h. The outline is drawn in colour, or white if omitted."; -const char doc222[] PROGMEM = "(draw-circle x y r [colour])\n" -"Draws an outline circle with its centre at (x, y) and with radius r.\n" -"The circle is drawn in colour, or white if omitted."; -const char doc223[] PROGMEM = "(fill-circle x y r [colour])\n" -"Draws a filled circle with its centre at (x, y) and with radius r.\n" -"The circle is drawn in colour, or white if omitted."; -const char doc224[] PROGMEM = "(draw-round-rect x y w h radius [colour])\n" -"Draws an outline rounded rectangle with its top left corner at (x,y), with width w,\n" -"height h, and corner radius radius. The outline is drawn in colour, or white if omitted."; -const char doc225[] PROGMEM = "(fill-round-rect x y w h radius [colour])\n" -"Draws a filled rounded rectangle with its top left corner at (x,y), with width w,\n" -"height h, and corner radius radius. The outline is drawn in colour, or white if omitted."; -const char doc226[] PROGMEM = "(draw-triangle x0 y0 x1 y1 x2 y2 [colour])\n" -"Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3).\n" -"The outline is drawn in colour, or white if omitted."; -const char doc227[] PROGMEM = "(fill-triangle x0 y0 x1 y1 x2 y2 [colour])\n" -"Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3).\n" -"The outline is drawn in colour, or white if omitted."; -const char doc228[] PROGMEM = "(draw-char x y char [colour background size])\n" -"Draws the character char with its top left corner at (x,y).\n" -"The character is drawn in a 5 x 7 pixel font in colour against background,\n" -"which default to white and black respectively.\n" -"The character can optionally be scaled by size."; -const char doc229[] PROGMEM = "(set-cursor x y)\n" -"Sets the start point for text plotting to (x, y)."; -const char doc230[] PROGMEM = "(set-text-color colour [background])\n" -"Sets the text colour for text plotted using (with-gfx ...)."; -const char doc231[] PROGMEM = "(set-text-size scale)\n" -"Scales text by the specified size, default 1."; -const char doc232[] PROGMEM = "(set-text-wrap boolean)\n" -"Specified whether text wraps at the right-hand edge of the display; the default is t."; -const char doc233[] PROGMEM = "(fill-screen [colour])\n" -"Fills or clears the screen with colour, default black."; -const char doc234[] PROGMEM = "(set-rotation option)\n" -"Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3."; -const char doc235[] PROGMEM = "(invert-display boolean)\n" -"Mirror-images the display."; - -// Built-in symbol lookup table -const tbl_entry_t lookup_table[] PROGMEM = { - { string0, NULL, 0000, doc0 }, - { string1, NULL, 0000, doc1 }, - { string2, NULL, 0000, doc2 }, - { string3, NULL, 0000, doc3 }, - { string4, NULL, 0000, doc4 }, - { string5, NULL, 0000, NULL }, - { string6, NULL, 0000, NULL }, - { string7, NULL, 0000, NULL }, - { string8, NULL, 0000, NULL }, - { string9, NULL, 0000, doc9 }, - { string10, NULL, 0017, doc10 }, - { string11, NULL, 0017, doc11 }, - { string12, NULL, 0017, doc12 }, - { string13, NULL, 0017, NULL }, - { string14, NULL, 0007, NULL }, - { string15, sp_quote, 0311, NULL }, - { string16, sp_defun, 0327, doc16 }, - { string17, sp_defvar, 0313, doc17 }, - { string18, fn_eq, 0222, doc18 }, - { string19, fn_car, 0211, doc19 }, - { string20, fn_car, 0211, NULL }, - { string21, fn_cdr, 0211, doc21 }, - { string22, fn_cdr, 0211, NULL }, - { string23, fn_nth, 0222, doc23 }, - { string24, fn_aref, 0227, doc24 }, - { string25, fn_char, 0222, doc25 }, - { string26, fn_stringfn, 0211, doc26 }, - { string27, fn_pinmode, 0222, doc27 }, - { string28, fn_digitalwrite, 0222, doc28 }, - { string29, fn_analogread, 0211, doc29 }, - { string30, fn_register, 0212, doc30 }, - { string31, fn_format, 0227, doc31 }, - { string32, sp_or, 0307, doc32 }, - { string33, sp_setq, 0327, doc33 }, - { string34, sp_loop, 0307, doc34 }, - { string35, sp_push, 0322, doc35 }, - { string36, sp_pop, 0311, doc36 }, - { string37, sp_incf, 0312, doc37 }, - { string38, sp_decf, 0312, doc38 }, - { string39, sp_setf, 0327, doc39 }, - { string40, sp_dolist, 0317, doc40 }, - { string41, sp_dotimes, 0317, doc41 }, - { string42, sp_do, 0327, doc42 }, - { string43, sp_dostar, 0317, doc43 }, - { string44, sp_trace, 0301, doc44 }, - { string45, sp_untrace, 0301, doc45 }, - { string46, sp_formillis, 0317, doc46 }, - { string47, sp_time, 0311, doc47 }, - { string48, sp_withoutputtostring, 0317, doc48 }, - { string49, sp_withserial, 0317, doc49 }, - { string50, sp_withi2c, 0317, doc50 }, - { string51, sp_withsdcard, 0327, doc51 }, - { string52, tf_progn, 0107, doc52 }, - { string53, tf_if, 0123, doc53 }, - { string54, tf_cond, 0107, doc54 }, - { string55, tf_when, 0117, doc55 }, - { string56, tf_unless, 0117, doc56 }, - { string57, tf_case, 0117, doc57 }, - { string58, tf_and, 0107, doc58 }, - { string59, fn_not, 0211, doc59 }, - { string60, fn_not, 0211, NULL }, - { string61, fn_cons, 0222, doc61 }, - { string62, fn_atom, 0211, doc62 }, - { string63, fn_listp, 0211, doc63 }, - { string64, fn_consp, 0211, doc64 }, - { string65, fn_symbolp, 0211, doc65 }, - { string66, fn_arrayp, 0211, doc66 }, - { string67, fn_boundp, 0211, doc67 }, - { string68, fn_keywordp, 0211, doc68 }, - { string69, fn_setfn, 0227, doc69 }, - { string70, fn_streamp, 0211, doc70 }, - { string71, fn_equal, 0222, doc71 }, - { string72, fn_caar, 0211, doc72 }, - { string73, fn_cadr, 0211, doc73 }, - { string74, fn_cadr, 0211, NULL }, - { string75, fn_cdar, 0211, doc75 }, - { string76, fn_cddr, 0211, doc76 }, - { string77, fn_caaar, 0211, doc77 }, - { string78, fn_caadr, 0211, doc78 }, - { string79, fn_cadar, 0211, doc79 }, - { string80, fn_caddr, 0211, doc80 }, - { string81, fn_caddr, 0211, NULL }, - { string82, fn_cdaar, 0211, doc82 }, - { string83, fn_cdadr, 0211, doc83 }, - { string84, fn_cddar, 0211, doc84 }, - { string85, fn_cdddr, 0211, doc85 }, - { string86, fn_length, 0211, doc86 }, - { string87, fn_arraydimensions, 0211, doc87 }, - { string88, fn_list, 0207, doc88 }, - { string89, fn_copylist, 0211, doc89 }, - { string90, fn_makearray, 0215, doc90 }, - { string91, fn_reverse, 0211, doc91 }, - { string92, fn_assoc, 0224, doc92 }, - { string93, fn_member, 0224, doc93 }, - { string94, fn_apply, 0227, doc94 }, - { string95, fn_funcall, 0217, doc95 }, - { string96, fn_append, 0207, doc96 }, - { string97, fn_mapc, 0227, doc97 }, - { string98, fn_mapl, 0227, doc98 }, - { string99, fn_mapcar, 0227, doc99 }, - { string100, fn_mapcan, 0227, doc100 }, - { string101, fn_maplist, 0227, doc101 }, - { string102, fn_mapcon, 0227, doc102 }, - { string103, fn_add, 0207, doc103 }, - { string104, fn_subtract, 0217, doc104 }, - { string105, fn_multiply, 0207, doc105 }, - { string106, fn_divide, 0217, doc106 }, - { string107, fn_mod, 0222, doc107 }, - { string108, fn_oneplus, 0211, doc108 }, - { string109, fn_oneminus, 0211, doc109 }, - { string110, fn_abs, 0211, doc110 }, - { string111, fn_random, 0211, doc111 }, - { string112, fn_maxfn, 0217, doc112 }, - { string113, fn_minfn, 0217, doc113 }, - { string114, fn_noteq, 0217, doc114 }, - { string115, fn_numeq, 0217, doc115 }, - { string116, fn_less, 0217, doc116 }, - { string117, fn_lesseq, 0217, doc117 }, - { string118, fn_greater, 0217, doc118 }, - { string119, fn_greatereq, 0217, doc119 }, - { string120, fn_plusp, 0211, doc120 }, - { string121, fn_minusp, 0211, doc121 }, - { string122, fn_zerop, 0211, doc122 }, - { string123, fn_oddp, 0211, doc123 }, - { string124, fn_evenp, 0211, doc124 }, - { string125, fn_integerp, 0211, doc125 }, - { string126, fn_numberp, 0211, doc126 }, - { string127, fn_floatfn, 0211, doc127 }, - { string128, fn_floatp, 0211, doc128 }, - { string129, fn_sin, 0211, doc129 }, - { string130, fn_cos, 0211, doc130 }, - { string131, fn_tan, 0211, doc131 }, - { string132, fn_asin, 0211, doc132 }, - { string133, fn_acos, 0211, doc133 }, - { string134, fn_atan, 0212, doc134 }, - { string135, fn_sinh, 0211, doc135 }, - { string136, fn_cosh, 0211, doc136 }, - { string137, fn_tanh, 0211, doc137 }, - { string138, fn_exp, 0211, doc138 }, - { string139, fn_sqrt, 0211, doc139 }, - { string140, fn_log, 0212, doc140 }, - { string141, fn_expt, 0222, doc141 }, - { string142, fn_ceiling, 0212, doc142 }, - { string143, fn_floor, 0212, doc143 }, - { string144, fn_truncate, 0212, doc144 }, - { string145, fn_round, 0212, doc145 }, - { string146, fn_charcode, 0211, doc146 }, - { string147, fn_codechar, 0211, doc147 }, - { string148, fn_characterp, 0211, doc148 }, - { string149, fn_stringp, 0211, doc149 }, - { string150, fn_stringeq, 0222, doc150 }, - { string151, fn_stringless, 0222, doc151 }, - { string152, fn_stringgreater, 0222, doc152 }, - { string153, fn_stringnoteq, 0222, doc153 }, - { string154, fn_stringlesseq, 0222, doc154 }, - { string155, fn_stringgreatereq, 0222, doc155 }, - { string156, fn_sort, 0222, doc156 }, - { string157, fn_concatenate, 0217, doc157 }, - { string158, fn_subseq, 0223, doc158 }, - { string159, fn_search, 0224, doc159 }, - { string160, fn_readfromstring, 0211, doc160 }, - { string161, fn_princtostring, 0211, doc161 }, - { string162, fn_prin1tostring, 0211, doc162 }, - { string163, fn_logand, 0207, doc163 }, - { string164, fn_logior, 0207, doc164 }, - { string165, fn_logxor, 0207, doc165 }, - { string166, fn_lognot, 0211, doc166 }, - { string167, fn_ash, 0222, doc167 }, - { string168, fn_logbitp, 0222, doc168 }, - { string169, fn_eval, 0211, doc169 }, - { string170, fn_return, 0201, doc170 }, - { string171, fn_globals, 0200, doc171 }, - { string172, fn_locals, 0200, doc172 }, - { string173, fn_makunbound, 0211, doc173 }, - { string174, fn_break, 0200, doc174 }, - { string175, fn_read, 0201, doc175 }, - { string176, fn_prin1, 0212, doc176 }, - { string177, fn_print, 0212, doc177 }, - { string178, fn_princ, 0212, doc178 }, - { string179, fn_terpri, 0201, doc179 }, - { string180, fn_readbyte, 0202, doc180 }, - { string181, fn_readline, 0201, doc181 }, - { string182, fn_writebyte, 0212, doc182 }, - { string183, fn_writestring, 0212, doc183 }, - { string184, fn_writeline, 0212, doc184 }, - { string185, fn_restarti2c, 0212, doc185 }, - { string186, fn_gc, 0200, doc186 }, - { string187, fn_room, 0200, doc187 }, - { string188, fn_saveimage, 0201, doc188 }, - { string189, fn_loadimage, 0201, doc189 }, - { string190, fn_cls, 0200, doc190 }, - { string191, fn_digitalread, 0211, doc191 }, - { string192, fn_analogreadresolution, 0211, doc192 }, - { string193, fn_analogwrite, 0222, doc193 }, - { string194, fn_delay, 0211, doc194 }, - { string195, fn_millis, 0200, doc195 }, - { string196, fn_sleep, 0201, doc196 }, - { string197, fn_note, 0203, doc197 }, - { string198, fn_edit, 0211, doc198 }, - { string199, fn_pprint, 0212, doc199 }, - { string200, fn_pprintall, 0201, doc200 }, - { string201, fn_require, 0211, doc201 }, - { string202, fn_listlibrary, 0200, doc202 }, - { string203, sp_help, 0311, doc203 }, - { string204, fn_documentation, 0212, doc204 }, - { string205, fn_apropos, 0211, doc205 }, - { string206, fn_aproposlist, 0211, doc206 }, - { string207, sp_unwindprotect, 0307, doc207 }, - { string208, sp_ignoreerrors, 0307, doc208 }, - { string209, sp_error, 0317, doc209 }, - { string210, sp_withclient, 0313, doc210 }, - { string211, fn_available, 0211, doc211 }, - { string212, fn_wifiserver, 0200, doc212 }, - { string213, fn_wifisoftap, 0204, doc213 }, - { string214, fn_connected, 0211, doc214 }, - { string215, fn_wifilocalip, 0200, doc215 }, - { string216, fn_wificonnect, 0203, doc216 }, - { string217, sp_withgfx, 0317, doc217 }, - { string218, fn_drawpixel, 0223, doc218 }, - { string219, fn_drawline, 0245, doc219 }, - { string220, fn_drawrect, 0245, doc220 }, - { string221, fn_fillrect, 0245, doc221 }, - { string222, fn_drawcircle, 0234, doc222 }, - { string223, fn_fillcircle, 0234, doc223 }, - { string224, fn_drawroundrect, 0256, doc224 }, - { string225, fn_fillroundrect, 0256, doc225 }, - { string226, fn_drawtriangle, 0267, doc226 }, - { string227, fn_filltriangle, 0267, doc227 }, - { string228, fn_drawchar, 0236, doc228 }, - { string229, fn_setcursor, 0222, doc229 }, - { string230, fn_settextcolor, 0212, doc230 }, - { string231, fn_settextsize, 0211, doc231 }, - { string232, fn_settextwrap, 0211, doc232 }, - { string233, fn_fillscreen, 0201, doc233 }, - { string234, fn_setrotation, 0211, doc234 }, - { string235, fn_invertdisplay, 0211, doc235 }, - { string236, (fn_ptr_type)LED_BUILTIN, 0, NULL }, - { string237, (fn_ptr_type)HIGH, DIGITALWRITE, NULL }, - { string238, (fn_ptr_type)LOW, DIGITALWRITE, NULL }, - { string239, (fn_ptr_type)INPUT, PINMODE, NULL }, - { string240, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL }, - { string241, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL }, - { string242, (fn_ptr_type)OUTPUT, PINMODE, NULL }, -}; - -#if !defined(extensions) -// Table cross-reference functions - -tbl_entry_t *tables[] = {lookup_table, NULL}; -const unsigned int tablesizes[] = { arraysize(lookup_table), 0 }; - -const tbl_entry_t *table (int n) { - return tables[n]; -} - -unsigned int tablesize (int n) { - return tablesizes[n]; -} -#endif - -// Table lookup functions - -/* - lookupbuiltin - looks up a string in lookup_table[], and returns the index of its entry, - or ENDFUNCTIONS if no match is found -*/ -builtin_t lookupbuiltin (char* c) { - unsigned int end = 0, start; - for (int n=0; n<2; n++) { - start = end; - int entries = tablesize(n); - end = end + entries; - for (int i=0; i> 3) & 0x07)) error2(toofewargs); - if ((minmax & 0x07) != 0x07 && nargs>(minmax & 0x07)) error2(toomanyargs); -} - -/* - lookupdoc - looks up the documentation string for the built-in function name -*/ -char *lookupdoc (builtin_t name) { - int n = namechars)>>((sizeof(int)-1)*8) & 0xFF) == ':'); -} - -/* - keywordp - check that obj is a keyword -*/ -bool keywordp (object *obj) { - if (!(symbolp(obj) && builtinp(obj->name))) return false; - builtin_t name = builtin(obj->name); - int n = name 4000) { delay(1); start = millis(); } -#endif - // Enough space? - if (Freespace <= WORKSPACESIZE>>4) gc(form, env); - // Escape - if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2("escape!");} - if (!tstflag(NOESC)) testescape(); - - if (form == NULL) return nil; - - if (form->type >= NUMBER && form->type <= STRING) return form; - - if (symbolp(form)) { - symbol_t name = form->name; - if (colonp(name)) return form; // Keyword - object *pair = value(name, env); - if (pair != NULL) return cdr(pair); - pair = value(name, GlobalEnv); - if (pair != NULL) return cdr(pair); - else if (builtinp(name)) { - if (builtin(name) == FEATURES) return features(); - return form; - } - Context = NIL; - error("undefined", form); - } - - // It's a list - object *function = car(form); - object *args = cdr(form); - - if (function == NULL) error("illegal function", nil); - if (!listp(args)) error("can't evaluate a dotted pair", args); - - // List starts with a builtin symbol? - if (symbolp(function) && builtinp(function->name)) { - builtin_t name = builtin(function->name); - - if ((name == LET) || (name == LETSTAR)) { - int TCstart = TC; - if (args == NULL) error2(noargument); - object *assigns = first(args); - if (!listp(assigns)) error(notalist, assigns); - object *forms = cdr(args); - object *newenv = env; - protect(newenv); - while (assigns != NULL) { - object *assign = car(assigns); - if (!consp(assign)) push(cons(assign,nil), newenv); - else if (cdr(assign) == NULL) push(cons(first(assign),nil), newenv); - else push(cons(first(assign),eval(second(assign),env)), newenv); - car(GCStack) = newenv; - if (name == LETSTAR) env = newenv; - assigns = cdr(assigns); - } - env = newenv; - unprotect(); - form = tf_progn(forms,env); - TC = TCstart; - goto EVAL; - } - - if (name == LAMBDA) { - if (env == NULL) return form; - object *envcopy = NULL; - while (env != NULL) { - object *pair = first(env); - if (pair != NULL) push(pair, envcopy); - env = cdr(env); - } - return cons(bsymbol(CLOSURE), cons(envcopy,args)); - } - uint8_t fntype = getminmax(name)>>6; - - if (fntype == SPECIAL_FORMS) { - Context = name; - checkargs(args); - return ((fn_ptr_type)lookupfn(name))(args, env); - } - - if (fntype == TAIL_FORMS) { - Context = name; - checkargs(args); - form = ((fn_ptr_type)lookupfn(name))(args, env); - TC = 1; - goto EVAL; - } - if (fntype == OTHER_FORMS) error("can't be used as a function", function); - } - - // Evaluate the parameters - result in head - object *fname = car(form); - int TCstart = TC; - object *head = cons(eval(fname, env), NULL); - protect(head); // Don't GC the result list - object *tail = head; - form = cdr(form); - int nargs = 0; - - while (form != NULL){ - object *obj = cons(eval(car(form),env),NULL); - cdr(tail) = obj; - tail = obj; - form = cdr(form); - nargs++; - } - - function = car(head); - args = cdr(head); - - if (symbolp(function)) { - builtin_t bname = builtin(function->name); - if (!builtinp(function->name)) error("not valid here", fname); - Context = bname; - checkminmax(bname, nargs); - object *result = ((fn_ptr_type)lookupfn(bname))(args, env); - unprotect(); - return result; - } - - if (consp(function)) { - symbol_t name = sym(NIL); - if (!listp(fname)) name = fname->name; - - if (isbuiltin(car(function), LAMBDA)) { - form = closure(TCstart, name, function, args, &env); - unprotect(); - int trace = tracing(fname->name); - if (trace) { - object *result = eval(form, env); - indent((--(TraceDepth[trace-1]))<<1, ' ', pserial); - pint(TraceDepth[trace-1], pserial); - pserial(':'); pserial(' '); - printobject(fname, pserial); pfstring(" returned ", pserial); - printobject(result, pserial); pln(pserial); - return result; - } else { - TC = 1; - goto EVAL; - } - } - - if (isbuiltin(car(function), CLOSURE)) { - function = cdr(function); - form = closure(TCstart, name, function, args, &env); - unprotect(); - TC = 1; - goto EVAL; - } - - } - error("illegal function", fname); return nil; -} - -// Print functions - -/* - pserial - prints a character to the serial port -*/ -void pserial (char c) { - LastPrint = c; - if (c == '\n') Serial.write('\r'); - Serial.write(c); -} - -const char ControlCodes[] = "Null\0SOH\0STX\0ETX\0EOT\0ENQ\0ACK\0Bell\0Backspace\0Tab\0Newline\0VT\0" -"Page\0Return\0SO\0SI\0DLE\0DC1\0DC2\0DC3\0DC4\0NAK\0SYN\0ETB\0CAN\0EM\0SUB\0Escape\0FS\0GS\0RS\0US\0Space\0"; - -/* - pcharacter - prints a character to a stream, escaping special characters if PRINTREADABLY is false - If <= 32 prints character name; eg #\Space - If < 127 prints ASCII; eg #\A - Otherwise prints decimal; eg #\234 -*/ -void pcharacter (uint8_t c, pfun_t pfun) { - if (!tstflag(PRINTREADABLY)) pfun(c); - else { - pfun('#'); pfun('\\'); - if (c <= 32) { - const char *p = ControlCodes; - while (c > 0) {p = p + strlen_P(p) + 1; c--; } - pfstring(p, pfun); - } else if (c < 127) pfun(c); - else pint(c, pfun); - } -} - -/* - pstring - prints a C string to the specified stream -*/ -void pstring (char *s, pfun_t pfun) { - while (*s) pfun(*s++); -} - -/* - plispstring - prints a Lisp string object to the specified stream -*/ -void plispstring (object *form, pfun_t pfun) { - plispstr(form->name, pfun); -} - -/* - plispstr - prints a Lisp string name to the specified stream -*/ -void plispstr (symbol_t name, pfun_t pfun) { - object *form = (object *)name; - while (form != NULL) { - int chars = form->chars; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - char ch = chars>>i & 0xFF; - if (tstflag(PRINTREADABLY) && (ch == '"' || ch == '\\')) pfun('\\'); - if (ch) pfun(ch); - } - form = car(form); - } -} - -/* - printstring - prints a Lisp string object to the specified stream - taking account of the PRINTREADABLY flag -*/ -void printstring (object *form, pfun_t pfun) { - if (tstflag(PRINTREADABLY)) pfun('"'); - plispstr(form->name, pfun); - if (tstflag(PRINTREADABLY)) pfun('"'); -} - -/* - pbuiltin - prints a built-in symbol to the specified stream -*/ -void pbuiltin (builtin_t name, pfun_t pfun) { - int n = name0; d = d/40) { - uint32_t j = x/d; - char c = fromradix40(j); - if (c == 0) return; - pfun(c); x = x - j*d; - } -} - -/* - printsymbol - prints any symbol from a symbol object to the specified stream -*/ -void printsymbol (object *form, pfun_t pfun) { - psymbol(form->name, pfun); -} - -/* - psymbol - prints any symbol from a symbol name to the specified stream -*/ -void psymbol (symbol_t name, pfun_t pfun) { - if (longnamep(name)) plispstr(name, pfun); - else { - uint32_t value = untwist(name); - if (value < PACKEDS) error2("invalid symbol"); - else if (value >= BUILTINS) pbuiltin((builtin_t)(value-BUILTINS), pfun); - else pradix40(name, pfun); - } -} - -/* - pfstring - prints a string from flash memory to the specified stream -*/ -void pfstring (const char *s, pfun_t pfun) { - while (1) { - char c = *s++; - if (c == 0) return; - pfun(c); - } -} - -/* - pint - prints an integer in decimal to the specified stream -*/ -void pint (int i, pfun_t pfun) { - uint32_t j = i; - if (i<0) { pfun('-'); j=-i; } - pintbase(j, 10, pfun); -} - -/* - pintbase - prints an integer in base 'base' to the specified stream -*/ -void pintbase (uint32_t i, uint8_t base, pfun_t pfun) { - int lead = 0; uint32_t p = 1000000000; - if (base == 2) p = 0x80000000; else if (base == 16) p = 0x10000000; - for (uint32_t d=p; d>0; d=d/base) { - uint32_t j = i/d; - if (j!=0 || lead || d==1) { pfun((j<10) ? j+'0' : j+'W'); lead=1;} - i = i - j*d; - } -} - -/* - pmantissa - prints the mantissa of a floating-point number to the specified stream -*/ -void pmantissa (float f, pfun_t pfun) { - int sig = floor(log10(f)); - int mul = pow(10, 5 - sig); - int i = round(f * mul); - bool point = false; - if (i == 1000000) { i = 100000; sig++; } - if (sig < 0) { - pfun('0'); pfun('.'); point = true; - for (int j=0; j < - sig - 1; j++) pfun('0'); - } - mul = 100000; - for (int j=0; j<7; j++) { - int d = (int)(i / mul); - pfun(d + '0'); - i = i - d * mul; - if (i == 0) { - if (!point) { - for (int k=j; k= 0) { pfun('.'); point = true; } - mul = mul / 10; - } -} - -/* - pfloat - prints a floating-point number to the specified stream -*/ -void pfloat (float f, pfun_t pfun) { - if (isnan(f)) { pfstring("NaN", pfun); return; } - if (f == 0.0) { pfun('0'); return; } - if (isinf(f)) { pfstring("Inf", pfun); return; } - if (f < 0) { pfun('-'); f = -f; } - // Calculate exponent - int e = 0; - if (f < 1e-3 || f >= 1e5) { - e = floor(log(f) / 2.302585); // log10 gives wrong result - f = f / pow(10, e); - } - - pmantissa (f, pfun); - - // Exponent - if (e != 0) { - pfun('e'); - pint(e, pfun); - } -} - -/* - pln - prints a newline to the specified stream -*/ -inline void pln (pfun_t pfun) { - pfun('\n'); -} - -/* - pfl - prints a newline to the specified stream if a newline has not just been printed -*/ -void pfl (pfun_t pfun) { - if (LastPrint != '\n') pfun('\n'); -} - -/* - plist - prints a list to the specified stream -*/ -void plist (object *form, pfun_t pfun) { - pfun('('); - printobject(car(form), pfun); - form = cdr(form); - while (form != NULL && listp(form)) { - pfun(' '); - printobject(car(form), pfun); - form = cdr(form); - } - if (form != NULL) { - pfstring(" . ", pfun); - printobject(form, pfun); - } - pfun(')'); -} - -/* - pstream - prints a stream name to the specified stream -*/ -void pstream (object *form, pfun_t pfun) { - pfun('<'); - pfstring(streamname[(form->integer)>>8], pfun); - pfstring("-stream ", pfun); - pint(form->integer & 0xFF, pfun); - pfun('>'); -} - -/* - printobject - prints any Lisp object to the specified stream -*/ -void printobject (object *form, pfun_t pfun) { - if (form == NULL) pfstring("nil", pfun); - else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring("", pfun); - else if (listp(form)) plist(form, pfun); - else if (integerp(form)) pint(form->integer, pfun); - else if (floatp(form)) pfloat(form->single_float, pfun); - else if (symbolp(form)) { if (form->name != sym(NOTHING)) printsymbol(form, pfun); } - else if (characterp(form)) pcharacter(form->chars, pfun); - else if (stringp(form)) printstring(form, pfun); - else if (arrayp(form)) printarray(form, pfun); - else if (streamp(form)) pstream(form, pfun); - else error2("error in print"); -} - -/* - prin1object - prints any Lisp object to the specified stream escaping special characters -*/ -void prin1object (object *form, pfun_t pfun) { - char temp = Flags; - clrflag(PRINTREADABLY); - printobject(form, pfun); - Flags = temp; -} - -// Read functions - -/* - glibrary - reads a character from the Lisp Library -*/ -int glibrary () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - char c = LispLibrary[GlobalStringIndex++]; - return (c != 0) ? c : -1; // -1? -} - -/* - loadfromlibrary - reads and evaluates a form from the Lisp Library -*/ -void loadfromlibrary (object *env) { - GlobalStringIndex = 0; - object *line = read(glibrary); - while (line != NULL) { - protect(line); - eval(line, env); - unprotect(); - line = read(glibrary); - } -} - -// For line editor -const int TerminalWidth = 80; -volatile int WritePtr = 0, ReadPtr = 0, LastWritePtr = 0; -const int KybdBufSize = 333; // 42*8 - 3 -char KybdBuf[KybdBufSize]; -volatile uint8_t KybdAvailable = 0; - -// Parenthesis highlighting -void esc (int p, char c) { - Serial.write('\e'); Serial.write('['); - Serial.write((char)('0'+ p/100)); - Serial.write((char)('0'+ (p/10) % 10)); - Serial.write((char)('0'+ p % 10)); - Serial.write(c); -} - -void hilight (char c) { - Serial.write('\e'); Serial.write('['); Serial.write(c); Serial.write('m'); -} - -/* - Highlight - handles parenthesis highlighting with the line editor -*/ -void Highlight (int p, int wp, uint8_t invert) { - wp = wp + 2; // Prompt -#if defined (printfreespace) - int f = Freespace; - while (f) { wp++; f=f/10; } -#endif - int line = wp/TerminalWidth; - int col = wp%TerminalWidth; - int targetline = (wp - p)/TerminalWidth; - int targetcol = (wp - p)%TerminalWidth; - int up = line-targetline, left = col-targetcol; - if (p) { - if (up) esc(up, 'A'); - if (col > targetcol) esc(left, 'D'); else esc(-left, 'C'); - if (invert) hilight('7'); - Serial.write('('); Serial.write('\b'); - // Go back - if (up) esc(up, 'B'); // Down - if (col > targetcol) esc(left, 'C'); else esc(-left, 'D'); - Serial.write('\b'); Serial.write(')'); - if (invert) hilight('0'); - } -} - -/* - processkey - handles keys in the line editor -*/ -void processkey (char c) { - if (c == 27) { setflag(ESCAPE); return; } // Escape key -#if defined(vt100) - static int parenthesis = 0, wp = 0; - // Undo previous parenthesis highlight - Highlight(parenthesis, wp, 0); - parenthesis = 0; -#endif - // Edit buffer - if (c == '\n' || c == '\r') { - pserial('\n'); - KybdAvailable = 1; - ReadPtr = 0; LastWritePtr = WritePtr; - return; - } - if (c == 8 || c == 0x7f) { // Backspace key - if (WritePtr > 0) { - WritePtr--; - Serial.write(8); Serial.write(' '); Serial.write(8); - if (WritePtr) c = KybdBuf[WritePtr-1]; - } - } else if (c == 9) { // tab or ctrl-I - for (int i = 0; i < LastWritePtr; i++) Serial.write(KybdBuf[i]); - WritePtr = LastWritePtr; - } else if (WritePtr < KybdBufSize) { - KybdBuf[WritePtr++] = c; - Serial.write(c); - } -#if defined(vt100) - // Do new parenthesis highlight - if (c == ')') { - int search = WritePtr-1, level = 0; - while (search >= 0 && parenthesis == 0) { - c = KybdBuf[search--]; - if (c == ')') level++; - if (c == '(') { - level--; - if (level == 0) {parenthesis = WritePtr-search-1; wp = WritePtr; } - } - } - Highlight(parenthesis, wp, 1); - } -#endif - return; -} - -/* - gserial - gets a character from the serial port -*/ -int gserial () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } -#if defined(lineeditor) - while (!KybdAvailable) { - while (!Serial.available()); - char temp = Serial.read(); - processkey(temp); - } - if (ReadPtr != WritePtr) return KybdBuf[ReadPtr++]; - KybdAvailable = 0; - WritePtr = 0; - return '\n'; -#else - unsigned long start = millis(); - while (!Serial.available()) { delay(1); if (millis() - start > 1000) clrflag(NOECHO); } - char temp = Serial.read(); - if (temp != '\n' && !tstflag(NOECHO)) pserial(temp); - return temp; -#endif -} - -/* - nextitem - reads the next token from the specified stream -*/ -object *nextitem (gfun_t gfun) { - int ch = gfun(); - while(issp(ch)) ch = gfun(); - - if (ch == ';') { - do { ch = gfun(); if (ch == ';' || ch == '(') setflag(NOECHO); } - while(ch != '('); - } - if (ch == '\n') ch = gfun(); - if (ch == -1) return nil; - if (ch == ')') return (object *)KET; - if (ch == '(') return (object *)BRA; - if (ch == '\'') return (object *)QUO; - - // Parse string - if (ch == '"') return readstring('"', true, gfun); - - // Parse symbol, character, or number - int index = 0, base = 10, sign = 1; - char buffer[BUFFERSIZE]; - int bufmax = BUFFERSIZE-3; // Max index - unsigned int result = 0; - bool isfloat = false; - float fresult = 0.0; - - if (ch == '+') { - buffer[index++] = ch; - ch = gfun(); - } else if (ch == '-') { - sign = -1; - buffer[index++] = ch; - ch = gfun(); - } else if (ch == '.') { - buffer[index++] = ch; - ch = gfun(); - if (ch == ' ') return (object *)DOT; - isfloat = true; - } - - // Parse reader macros - else if (ch == '#') { - ch = gfun(); - char ch2 = ch & ~0x20; // force to upper case - if (ch == '\\') { // Character - base = 0; ch = gfun(); - if (issp(ch) || isbr(ch)) return character(ch); - else LastChar = ch; - } else if (ch == '|') { - do { while (gfun() != '|'); } - while (gfun() != '#'); - return nextitem(gfun); - } else if (ch2 == 'B') base = 2; - else if (ch2 == 'O') base = 8; - else if (ch2 == 'X') base = 16; - else if (ch == '\'') return nextitem(gfun); - else if (ch == '.') { - setflag(NOESC); - object *result = eval(read(gfun), NULL); - clrflag(NOESC); - return result; - } - else if (ch == '(') { LastChar = ch; return readarray(1, read(gfun)); } - else if (ch == '*') return readbitarray(gfun); - else if (ch >= '1' && ch <= '9' && (gfun() & ~0x20) == 'A') return readarray(ch - '0', read(gfun)); - else error2("illegal character after #"); - ch = gfun(); - } - int valid; // 0=undecided, -1=invalid, +1=valid - if (ch == '.') valid = 0; else if (digitvalue(ch) ((unsigned int)INT_MAX+(1-sign)/2)) - return makefloat((float)result*sign); - return number(result*sign); - } else if (base == 0) { - if (index == 1) return character(buffer[0]); - const char *p = ControlCodes; char c = 0; - while (c < 33) { - if (strcasecmp(buffer, p) == 0) return character(c); - p = p + strlen(p) + 1; c++; - } - if (index == 3) return character((buffer[0]*10+buffer[1])*10+buffer[2]-5328); - error2("unknown character"); - } - - builtin_t x = lookupbuiltin(buffer); - if (x == NIL) return nil; - if (x != ENDFUNCTIONS) return bsymbol(x); - if (index <= 6 && valid40(buffer)) return intern(twist(pack40(buffer))); - return internlong(buffer); -} - -/* - readrest - reads the remaining tokens from the specified stream -*/ -object *readrest (gfun_t gfun) { - object *item = nextitem(gfun); - object *head = NULL; - object *tail = NULL; - - while (item != (object *)KET) { - if (item == (object *)BRA) { - item = readrest(gfun); - } else if (item == (object *)QUO) { - item = cons(bsymbol(QUOTE), cons(read(gfun), NULL)); - } else if (item == (object *)DOT) { - tail->cdr = read(gfun); - if (readrest(gfun) != NULL) error2("malformed list"); - return head; - } else { - object *cell = cons(item, NULL); - if (head == NULL) head = cell; - else tail->cdr = cell; - tail = cell; - item = nextitem(gfun); - } - } - return head; -} - -/* - read - recursively reads a Lisp object from the stream gfun and returns it -*/ -object *read (gfun_t gfun) { - object *item = nextitem(gfun); - if (item == (object *)KET) error2("incomplete list"); - if (item == (object *)BRA) return readrest(gfun); - if (item == (object *)DOT) return read(gfun); - if (item == (object *)QUO) return cons(bsymbol(QUOTE), cons(read(gfun), NULL)); - return item; -} - -// Setup - -/* - initenv - initialises the uLisp environment -*/ -void initenv () { - GlobalEnv = NULL; - tee = bsymbol(TEE); -} - -/* - initgfx - initialises the graphics -*/ -void initgfx () { - #if defined(gfxsupport) - tft.init(135, 240); - #if defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) - pinMode(TFT_I2C_POWER, OUTPUT); - digitalWrite(TFT_I2C_POWER, HIGH); - tft.setRotation(3); - #else - tft.setRotation(1); - #endif - tft.fillScreen(ST77XX_BLACK); - pinMode(TFT_BACKLITE, OUTPUT); - digitalWrite(TFT_BACKLITE, HIGH); - #endif -} - -// Entry point from the Arduino IDE -void setup () { - Serial.begin(9600); - int start = millis(); - while ((millis() - start) < 5000) { if (Serial) break; } - initworkspace(); - initenv(); - initsleep(); - initgfx(); - pfstring(PSTR("uLisp 4.6 "), pserial); pln(pserial); -} - -// Read/Evaluate/Print loop - -/* - repl - the Lisp Read/Evaluate/Print loop -*/ -void repl (object *env) { - for (;;) { - randomSeed(micros()); - gc(NULL, env); - #if defined(printfreespace) - pint(Freespace, pserial); - #endif - if (BreakLevel) { - pfstring(" : ", pserial); - pint(BreakLevel, pserial); - } - pserial('>'); pserial(' '); - Context = NIL; - object *line = read(gserial); - if (BreakLevel && line == nil) { pln(pserial); return; } - if (line == (object *)KET) error2("unmatched right bracket"); - protect(line); - pfl(pserial); - line = eval(line, env); - pfl(pserial); - printobject(line, pserial); - unprotect(); - pfl(pserial); - pln(pserial); - } -} - -/* - loop - the Arduino IDE main execution loop -*/ -void loop () { - if (!setjmp(toplevel_handler)) { - #if defined(resetautorun) - volatile int autorun = 12; // Fudge to keep code size the same - #else - volatile int autorun = 13; - #endif - if (autorun == 12) autorunimage(); - } - ulisperror(); - repl(NULL); -} - -void ulisperror () { - // Come here after error - delay(100); while (Serial.available()) Serial.read(); - clrflag(NOESC); BreakLevel = 0; - for (int i=0; i -#include -#include -#include -#include - -#if defined(gfxsupport) -#define COLOR_WHITE ST77XX_WHITE -#define COLOR_BLACK ST77XX_BLACK -#include // Core graphics library -#include // Hardware-specific library for ST7789 -#if defined(ARDUINO_ESP32_DEV) -Adafruit_ST7789 tft = Adafruit_ST7789(5, 16, 19, 18); -#define TFT_BACKLITE 4 -#else -Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, MOSI, SCK, TFT_RST); -#endif -#endif - -#if defined(sdcardsupport) - #include - #define SDSIZE 172 -#else - #define SDSIZE 0 -#endif - -// Platform specific settings - -#define WORDALIGNED __attribute__((aligned (4))) -#define BUFFERSIZE 36 // Number of bits+4 - -#if defined(ARDUINO_FEATHER_ESP32) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) - #define WORKSPACESIZE (8160-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) - #define WORKSPACESIZE (8160-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_FEATHERS2) /* UM FeatherS2 */ - #define WORKSPACESIZE (8160-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32_DEV) /* For TTGO T-Display */ - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32S2_DEV) - #define WORKSPACESIZE (8100-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32C3_DEV) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ARDUINO_ESP32S3_DEV) - #define WORKSPACESIZE (22000-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#elif defined(ESP32) - #define WORKSPACESIZE (9216-SDSIZE) /* Cells (8*bytes) */ - #define LITTLEFS - #include - #define analogWrite(x,y) dacWrite((x),(y)) - #define SDCARD_SS_PIN 13 - #define LED_BUILTIN 13 - -#else -#error "Board not supported!" -#endif - -// C Macros - -#define nil NULL -#define car(x) (((object *) (x))->car) -#define cdr(x) (((object *) (x))->cdr) - -#define first(x) car(x) -#define rest(x) cdr(x) -#define second(x) first(rest(x)) -#define cddr(x) cdr(cdr(x)) -#define third(x) first(cddr(x)) - -#define push(x, y) ((y) = cons((x),(y))) -#define pop(y) ((y) = cdr(y)) - -#define protect(y) push((y), GCStack) -#define unprotect() pop(GCStack) - -#define integerp(x) ((x) != NULL && (x)->type == NUMBER) -#define floatp(x) ((x) != NULL && (x)->type == FLOAT) -#define symbolp(x) ((x) != NULL && (x)->type == SYMBOL) -#define stringp(x) ((x) != NULL && (x)->type == STRING) -#define characterp(x) ((x) != NULL && (x)->type == CHARACTER) -#define arrayp(x) ((x) != NULL && (x)->type == ARRAY) -#define streamp(x) ((x) != NULL && (x)->type == STREAM) - -#define mark(x) (car(x) = (object *)(((uintptr_t)(car(x))) | MARKBIT)) -#define unmark(x) (car(x) = (object *)(((uintptr_t)(car(x))) & ~MARKBIT)) -#define marked(x) ((((uintptr_t)(car(x))) & MARKBIT) != 0) -#define MARKBIT 1 - -#define setflag(x) (Flags |= 1<<(x)) -#define clrflag(x) (Flags &= ~(1<<(x))) -#define tstflag(x) (Flags & 1<<(x)) - -#define issp(x) (x == ' ' || x == '\n' || x == '\r' || x == '\t') -#define isbr(x) (x == ')' || x == '(' || x == '"' || x == '#') -#define longsymbolp(x) (((x)->name & 0x03) == 0) -#define longnamep(x) (((x) & 0x03) == 0) -#define arraysize(x) (sizeof(x) / sizeof(x[0])) -#define stringifyX(x) #x -#define stringify(x) stringifyX(x) -#define PACKEDS 0x43238000 -#define BUILTINS 0xF4240000 -#define ENDFUNCTIONS 1536 - -// Constants - -const int TRACEMAX = 3; // Number of traced functions -enum type { ZZERO=0, SYMBOL=2, CODE=4, NUMBER=6, STREAM=8, CHARACTER=10, FLOAT=12, ARRAY=14, STRING=16, PAIR=18 }; // ARRAY STRING and PAIR must be last -enum token { UNUSED, BRA, KET, QUO, DOT }; -enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM, WIFISTREAM, STRINGSTREAM, GFXSTREAM }; -enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS }; - -// Stream names used by printobject -const char serialstream[] = "serial"; -const char i2cstream[] = "i2c"; -const char spistream[] = "spi"; -const char sdstream[] = "sd"; -const char wifistream[] = "wifi"; -const char stringstream[] = "string"; -const char gfxstream[] = "gfx"; -const char *const streamname[] = {serialstream, i2cstream, spistream, sdstream, wifistream, stringstream, gfxstream}; - -// Typedefs - -typedef uint32_t symbol_t; -typedef uint32_t builtin_t; -typedef uint32_t chars_t; - -typedef struct sobject { - union { - struct { - sobject *car; - sobject *cdr; - }; - struct { - unsigned int type; - union { - symbol_t name; - int integer; - chars_t chars; // For strings - float single_float; - }; - }; - }; -} object; - -typedef object *(*fn_ptr_type)(object *, object *); -typedef void (*mapfun_t)(object *, object **); - -typedef const struct { - const char *string; - fn_ptr_type fptr; - uint8_t minmax; - const char *doc; -} tbl_entry_t; - -typedef int (*gfun_t)(); -typedef void (*pfun_t)(char); - -enum builtins: builtin_t { NIL, TEE, NOTHING, OPTIONAL, FEATURES, INITIALELEMENT, ELEMENTTYPE, TEST, BIT, AMPREST, -LAMBDA, LET, LETSTAR, CLOSURE, PSTAR, QUOTE, DEFUN, DEFVAR, EQ, CAR, FIRST, CDR, REST, NTH, AREF, CHAR, -STRINGFN, PINMODE, DIGITALWRITE, ANALOGREAD, REGISTER, FORMAT, - }; - -// Global variables - -object Workspace[WORKSPACESIZE] WORDALIGNED; - -jmp_buf toplevel_handler; -jmp_buf *handler = &toplevel_handler; -unsigned int Freespace = 0; -object *Freelist; -unsigned int I2Ccount; -unsigned int TraceFn[TRACEMAX]; -unsigned int TraceDepth[TRACEMAX]; -builtin_t Context; - -object *GlobalEnv; -object *GCStack = NULL; -object *GlobalString; -object *GlobalStringTail; -int GlobalStringIndex = 0; -uint8_t PrintCount = 0; -uint8_t BreakLevel = 0; -char LastChar = 0; -char LastPrint = 0; - -// Flags -enum flag { PRINTREADABLY, RETURNFLAG, ESCAPE, EXITEDITOR, LIBRARYLOADED, NOESC, NOECHO, MUFFLEERRORS }; -volatile uint8_t Flags = 0b00001; // PRINTREADABLY set by default - -// Forward references -object *tee; -void pfstring (const char *s, pfun_t pfun); - -inline symbol_t twist (builtin_t x) { - return (x<<2) | ((x & 0xC0000000)>>30); -} - -inline builtin_t untwist (symbol_t x) { - return (x>>2 & 0x3FFFFFFF) | ((x & 0x03)<<30); -} - -// Error handling - -void errorsub (symbol_t fname, const char *string) { - pfl(pserial); pfstring("Error: ", pserial); - if (fname != sym(NIL)) { - pserial('\''); - psymbol(fname, pserial); - pserial('\''); pserial(' '); - } - pfstring(string, pserial); -} - -void errorend () { GCStack = NULL; longjmp(*handler, 1); } - -void errorsym (symbol_t fname, const char *string, object *symbol) { - if (!tstflag(MUFFLEERRORS)) { - errorsub(fname, string); - pserial(':'); pserial(' '); - printobject(symbol, pserial); - pln(pserial); - } - errorend(); -} - -void errorsym2 (symbol_t fname, const char *string) { - if (!tstflag(MUFFLEERRORS)) { - errorsub(fname, string); - pln(pserial); - } - errorend(); -} - -void error (const char *string, object *symbol) { - errorsym(sym(Context), string, symbol); -} - -void error2 (const char *string) { - errorsym2(sym(Context), string); -} - -void formaterr (object *formatstr, const char *string, uint8_t p) { - pln(pserial); indent(4, ' ', pserial); printstring(formatstr, pserial); pln(pserial); - indent(p+5, ' ', pserial); pserial('^'); - error2(string); - pln(pserial); - GCStack = NULL; - longjmp(*handler, 1); -} - -// Save space as these are used multiple times -const char notanumber[] = "argument is not a number"; -const char notaninteger[] = "argument is not an integer"; -const char notastring[] = "argument is not a string"; -const char notalist[] = "argument is not a list"; -const char notasymbol[] = "argument is not a symbol"; -const char notproper[] = "argument is not a proper list"; -const char toomanyargs[] = "too many arguments"; -const char toofewargs[] = "too few arguments"; -const char noargument[] = "missing argument"; -const char nostream[] = "missing stream argument"; -const char overflow[] = "arithmetic overflow"; -const char divisionbyzero[] = "division by zero"; -const char indexnegative[] = "index can't be negative"; -const char invalidarg[] = "invalid argument"; -const char invalidkey[] = "invalid keyword"; -const char illegalclause[] = "illegal clause"; -const char invalidpin[] = "invalid pin"; -const char oddargs[] = "odd number of arguments"; -const char indexrange[] = "index out of range"; -const char canttakecar[] = "can't take car"; -const char canttakecdr[] = "can't take cdr"; -const char unknownstreamtype[] = "unknown stream type"; - -// Set up workspace - -void initworkspace () { - Freelist = NULL; - for (int i=WORKSPACESIZE-1; i>=0; i--) { - object *obj = &Workspace[i]; - car(obj) = NULL; - cdr(obj) = Freelist; - Freelist = obj; - Freespace++; - } -} - -object *myalloc () { - if (Freespace == 0) { Context = NIL; error2("no room"); } - object *temp = Freelist; - Freelist = cdr(Freelist); - Freespace--; - return temp; -} - -inline void myfree (object *obj) { - car(obj) = NULL; - cdr(obj) = Freelist; - Freelist = obj; - Freespace++; -} - -// Make each type of object - -object *number (int n) { - object *ptr = myalloc(); - ptr->type = NUMBER; - ptr->integer = n; - return ptr; -} - -object *makefloat (float f) { - object *ptr = myalloc(); - ptr->type = FLOAT; - ptr->single_float = f; - return ptr; -} - -object *character (uint8_t c) { - object *ptr = myalloc(); - ptr->type = CHARACTER; - ptr->chars = c; - return ptr; -} - -object *cons (object *arg1, object *arg2) { - object *ptr = myalloc(); - ptr->car = arg1; - ptr->cdr = arg2; - return ptr; -} - -object *symbol (symbol_t name) { - object *ptr = myalloc(); - ptr->type = SYMBOL; - ptr->name = name; - return ptr; -} - -inline object *bsymbol (builtin_t name) { - return intern(twist(name+BUILTINS)); -} - -object *intern (symbol_t name) { - for (int i=0; itype == SYMBOL && obj->name == name) return obj; - } - return symbol(name); -} - -bool eqsymbols (object *obj, char *buffer) { - object *arg = cdr(obj); - int i = 0; - while (!(arg == NULL && buffer[i] == 0)) { - if (arg == NULL || buffer[i] == 0) return false; - int test = 0, shift = 24; - for (int j=0; j<4; j++, i++) { - if (buffer[i] == 0) break; - test = test | buffer[i]<chars != test) return false; - arg = car(arg); - } - return true; -} - -object *internlong (char *buffer) { - for (int i=0; itype == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) return obj; - } - object *obj = lispstring(buffer); - obj->type = SYMBOL; - return obj; -} - -object *stream (uint8_t streamtype, uint8_t address) { - object *ptr = myalloc(); - ptr->type = STREAM; - ptr->integer = streamtype<<8 | address; - return ptr; -} - -object *newstring () { - object *ptr = myalloc(); - ptr->type = STRING; - ptr->chars = 0; - return ptr; -} - -// Features - -const char floatingpoint[] = ":floating-point"; -const char arrays[] = ":arrays"; -const char doc[] = ":documentation"; -const char errorhandling[] = ":error-handling"; -const char wifi[] = ":wi-fi"; -const char gfx[] = ":gfx"; - -object *features () { - object *result = NULL; - push(internlong((char *)gfx), result); - push(internlong((char *)wifi), result); - push(internlong((char *)errorhandling), result); - push(internlong((char *)doc), result); - push(internlong((char *)arrays), result); - push(internlong((char *)floatingpoint), result); - return result; -} - -// Garbage collection - -void markobject (object *obj) { - MARK: - if (obj == NULL) return; - if (marked(obj)) return; - - object* arg = car(obj); - unsigned int type = obj->type; - mark(obj); - - if (type >= PAIR || type == ZZERO) { // cons - markobject(arg); - obj = cdr(obj); - goto MARK; - } - - if (type == ARRAY) { - obj = cdr(obj); - goto MARK; - } - - if ((type == STRING) || (type == SYMBOL && longsymbolp(obj))) { - obj = cdr(obj); - while (obj != NULL) { - arg = car(obj); - mark(obj); - obj = arg; - } - } -} - -void sweep () { - Freelist = NULL; - Freespace = 0; - for (int i=WORKSPACESIZE-1; i>=0; i--) { - object *obj = &Workspace[i]; - if (!marked(obj)) myfree(obj); else unmark(obj); - } -} - -void gc (object *form, object *env) { - #if defined(printgcs) - int start = Freespace; - #endif - markobject(tee); - markobject(GlobalEnv); - markobject(GCStack); - markobject(form); - markobject(env); - sweep(); - #if defined(printgcs) - pfl(pserial); pserial('{'); pint(Freespace - start, pserial); pserial('}'); - #endif -} - -// Compact image - -void movepointer (object *from, object *to) { - for (int i=0; itype) & ~MARKBIT; - if (marked(obj) && (type >= ARRAY || type==ZZERO || (type == SYMBOL && longsymbolp(obj)))) { - if (car(obj) == (object *)((uintptr_t)from | MARKBIT)) - car(obj) = (object *)((uintptr_t)to | MARKBIT); - if (cdr(obj) == from) cdr(obj) = to; - } - } - // Fix strings and long symbols - for (int i=0; itype) & ~MARKBIT; - if (type == STRING || (type == SYMBOL && longsymbolp(obj))) { - obj = cdr(obj); - while (obj != NULL) { - if (cdr(obj) == to) cdr(obj) = from; - obj = (object *)((uintptr_t)(car(obj)) & ~MARKBIT); - } - } - } - } -} - -uintptr_t compactimage (object **arg) { - markobject(tee); - markobject(GlobalEnv); - markobject(GCStack); - object *firstfree = Workspace; - while (marked(firstfree)) firstfree++; - object *obj = &Workspace[WORKSPACESIZE-1]; - while (firstfree < obj) { - if (marked(obj)) { - car(firstfree) = car(obj); - cdr(firstfree) = cdr(obj); - unmark(obj); - movepointer(obj, firstfree); - if (GlobalEnv == obj) GlobalEnv = firstfree; - if (GCStack == obj) GCStack = firstfree; - if (*arg == obj) *arg = firstfree; - while (marked(firstfree)) firstfree++; - } - obj--; - } - sweep(); - return firstfree - Workspace; -} - -// Make SD card filename - -char *MakeFilename (object *arg, char *buffer) { - int max = BUFFERSIZE-1; - buffer[0]='/'; - int i = 1; - do { - char c = nthchar(arg, i-1); - if (c == '\0') break; - buffer[i++] = c; - } while (i>8 & 0xFF); - file.write(data>>16 & 0xFF); file.write(data>>24 & 0xFF); -} - -int SDReadInt (File file) { - uintptr_t b0 = file.read(); uintptr_t b1 = file.read(); - uintptr_t b2 = file.read(); uintptr_t b3 = file.read(); - return b0 | b1<<8 | b2<<16 | b3<<24; -} -#elif defined(LITTLEFS) -void FSWrite32 (File file, uint32_t data) { - union { uint32_t data2; uint8_t u8[4]; }; - data2 = data; - if (file.write(u8, 4) != 4) error2("not enough room"); -} - -uint32_t FSRead32 (File file) { - union { uint32_t data; uint8_t u8[4]; }; - file.read(u8, 4); - return data; -} -#else -void EpromWriteInt(int *addr, uintptr_t data) { - EEPROM.write((*addr)++, data & 0xFF); EEPROM.write((*addr)++, data>>8 & 0xFF); - EEPROM.write((*addr)++, data>>16 & 0xFF); EEPROM.write((*addr)++, data>>24 & 0xFF); -} - -int EpromReadInt (int *addr) { - uint8_t b0 = EEPROM.read((*addr)++); uint8_t b1 = EEPROM.read((*addr)++); - uint8_t b2 = EEPROM.read((*addr)++); uint8_t b3 = EEPROM.read((*addr)++); - return b0 | b1<<8 | b2<<16 | b3<<24; -} -#endif - -unsigned int saveimage (object *arg) { -#if defined(sdcardsupport) - unsigned int imagesize = compactimage(&arg); - SD.begin(SDCARD_SS_PIN); - File file; - if (stringp(arg)) { - char buffer[BUFFERSIZE]; - file = SD.open(MakeFilename(arg, buffer), FILE_WRITE); - if (!file) error2("problem saving to SD card or invalid filename"); - arg = NULL; - } else if (arg == NULL || listp(arg)) { - file = SD.open("/ULISP.IMG", FILE_WRITE); - if (!file) error2("problem saving to SD card"); - } else error(invalidarg, arg); - SDWriteInt(file, (uintptr_t)arg); - SDWriteInt(file, imagesize); - SDWriteInt(file, (uintptr_t)GlobalEnv); - SDWriteInt(file, (uintptr_t)GCStack); - for (unsigned int i=0; i EEPROMSIZE) error("image too large", number(imagesize)); - EEPROM.begin(EEPROMSIZE); - int addr = 0; - EpromWriteInt(&addr, (uintptr_t)arg); - EpromWriteInt(&addr, imagesize); - EpromWriteInt(&addr, (uintptr_t)GlobalEnv); - EpromWriteInt(&addr, (uintptr_t)GCStack); - for (unsigned int i=0; itype; - return type >= PAIR || type == ZZERO; -} - -#define atom(x) (!consp(x)) - -bool listp (object *x) { - if (x == NULL) return true; - unsigned int type = x->type; - return type >= PAIR || type == ZZERO; -} - -#define improperp(x) (!listp(x)) - -object *quote (object *arg) { - return cons(bsymbol(QUOTE), cons(arg,NULL)); -} - -// Radix 40 encoding - -builtin_t builtin (symbol_t name) { - return (builtin_t)(untwist(name) - BUILTINS); -} - -symbol_t sym (builtin_t x) { - return twist(x + BUILTINS); -} - -int8_t toradix40 (char ch) { - if (ch == 0) return 0; - if (ch >= '0' && ch <= '9') return ch-'0'+1; - if (ch == '-') return 37; if (ch == '*') return 38; if (ch == '$') return 39; - ch = ch | 0x20; - if (ch >= 'a' && ch <= 'z') return ch-'a'+11; - return -1; // Invalid -} - -char fromradix40 (char n) { - if (n >= 1 && n <= 10) return '0'+n-1; - if (n >= 11 && n <= 36) return 'a'+n-11; - if (n == 37) return '-'; if (n == 38) return '*'; if (n == 39) return '$'; - return 0; -} - -uint32_t pack40 (char *buffer) { - int x = 0, j = 0; - for (int i=0; i<6; i++) { - x = x * 40 + toradix40(buffer[j]); - if (buffer[j] != 0) j++; - } - return x; -} - -bool valid40 (char *buffer) { - int t = 11; - for (int i=0; i<6; i++) { - if (toradix40(buffer[i]) < t) return false; - if (buffer[i] == 0) break; - t = 0; - } - return true; -} - -int8_t digitvalue (char d) { - if (d>='0' && d<='9') return d-'0'; - d = d | 0x20; - if (d>='a' && d<='f') return d-'a'+10; - return 16; -} - -int checkinteger (object *obj) { - if (!integerp(obj)) error(notaninteger, obj); - return obj->integer; -} - -int checkbitvalue (object *obj) { - if (!integerp(obj)) error(notaninteger, obj); - int n = obj->integer; - if (n & ~1) error("argument is not a bit value", obj); - return n; -} - -float checkintfloat (object *obj) { - if (integerp(obj)) return (float)obj->integer; - if (!floatp(obj)) error(notanumber, obj); - return obj->single_float; -} - -int checkchar (object *obj) { - if (!characterp(obj)) error("argument is not a character", obj); - return obj->chars; -} - -object *checkstring (object *obj) { - if (!stringp(obj)) error(notastring, obj); - return obj; -} - -int isstream (object *obj){ - if (!streamp(obj)) error("not a stream", obj); - return obj->integer; -} - -int isbuiltin (object *obj, builtin_t n) { - return symbolp(obj) && obj->name == sym(n); -} - -bool builtinp (symbol_t name) { - return (untwist(name) >= BUILTINS); -} - -int checkkeyword (object *obj) { - if (!keywordp(obj)) error("argument is not a keyword", obj); - builtin_t kname = builtin(obj->name); - uint8_t context = getminmax(kname); - if (context != 0 && context != Context) error(invalidkey, obj); - return ((int)lookupfn(kname)); -} - -void checkargs (object *args) { - int nargs = listlength(args); - checkminmax(Context, nargs); -} - -boolean eq (object *arg1, object *arg2) { - if (arg1 == arg2) return true; // Same object - if ((arg1 == nil) || (arg2 == nil)) return false; // Not both values - if (arg1->cdr != arg2->cdr) return false; // Different values - if (symbolp(arg1) && symbolp(arg2)) return true; // Same symbol - if (integerp(arg1) && integerp(arg2)) return true; // Same integer - if (floatp(arg1) && floatp(arg2)) return true; // Same float - if (characterp(arg1) && characterp(arg2)) return true; // Same character - return false; -} - -bool equal (object *arg1, object *arg2) { - if (stringp(arg1) && stringp(arg2)) return (stringcompare(cons(arg1, cons(arg2, nil)), false, false, true) != -1); - if (consp(arg1) && consp(arg2)) return (equal(car(arg1), car(arg2)) && equal(cdr(arg1), cdr(arg2))); - return eq(arg1, arg2); -} - -int listlength (object *list) { - int length = 0; - while (list != NULL) { - if (improperp(list)) error2(notproper); - list = cdr(list); - length++; - } - return length; -} - -object *checkarguments (object *args, int min, int max) { - if (args == NULL) error2(noargument); - args = first(args); - if (!listp(args)) error(notalist, args); - int length = listlength(args); - if (length < min) error(toofewargs, args); - if (length > max) error(toomanyargs, args); - return args; -} - -// Mathematical helper functions - -object *add_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - fresult = fresult + checkintfloat(arg); - args = cdr(args); - } - return makefloat(fresult); -} - -object *subtract_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - fresult = fresult - checkintfloat(arg); - args = cdr(args); - } - return makefloat(fresult); -} - -object *negate (object *arg) { - if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MIN) return makefloat(-result); - else return number(-result); - } else if (floatp(arg)) return makefloat(-(arg->single_float)); - else error(notanumber, arg); - return nil; -} - -object *multiply_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - fresult = fresult * checkintfloat(arg); - args = cdr(args); - } - return makefloat(fresult); -} - -object *divide_floats (object *args, float fresult) { - while (args != NULL) { - object *arg = car(args); - float f = checkintfloat(arg); - if (f == 0.0) error2(divisionbyzero); - fresult = fresult / f; - args = cdr(args); - } - return makefloat(fresult); -} - -object *compare (object *args, bool lt, bool gt, bool eq) { - object *arg1 = first(args); - args = cdr(args); - while (args != NULL) { - object *arg2 = first(args); - if (integerp(arg1) && integerp(arg2)) { - if (!lt && ((arg1->integer) < (arg2->integer))) return nil; - if (!eq && ((arg1->integer) == (arg2->integer))) return nil; - if (!gt && ((arg1->integer) > (arg2->integer))) return nil; - } else { - if (!lt && (checkintfloat(arg1) < checkintfloat(arg2))) return nil; - if (!eq && (checkintfloat(arg1) == checkintfloat(arg2))) return nil; - if (!gt && (checkintfloat(arg1) > checkintfloat(arg2))) return nil; - } - arg1 = arg2; - args = cdr(args); - } - return tee; -} - -int intpower (int base, int exp) { - int result = 1; - while (exp) { - if (exp & 1) result = result * base; - exp = exp / 2; - base = base * base; - } - return result; -} - -// Association lists - -object *testargument (object *args) { - object *test = bsymbol(EQ); - if (args != NULL) { - if (cdr(args) == NULL) error2("unpaired keyword"); - if ((isbuiltin(first(args), TEST))) test = second(args); - else error("unsupported keyword", first(args)); - } - return test; -} - -object *delassoc (object *key, object **alist) { - object *list = *alist; - object *prev = NULL; - while (list != NULL) { - object *pair = first(list); - if (eq(key,car(pair))) { - if (prev == NULL) *alist = cdr(list); - else cdr(prev) = cdr(list); - return key; - } - prev = list; - list = cdr(list); - } - return nil; -} - -// Array utilities - -int nextpower2 (int n) { - n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; - n |= n >> 8; n |= n >> 16; n++; - return n<2 ? 2 : n; -} - -object *buildarray (int n, int s, object *def) { - int s2 = s>>1; - if (s2 == 1) { - if (n == 2) return cons(def, def); - else if (n == 1) return cons(def, NULL); - else return NULL; - } else if (n >= s2) return cons(buildarray(s2, s2, def), buildarray(n - s2, s2, def)); - else return cons(buildarray(n, s2, def), nil); -} - -object *makearray (object *dims, object *def, bool bitp) { - int size = 1; - object *dimensions = dims; - while (dims != NULL) { - int d = car(dims)->integer; - if (d < 0) error2("dimension can't be negative"); - size = size * d; - dims = cdr(dims); - } - // Bit array identified by making first dimension negative - if (bitp) { - size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - car(dimensions) = number(-(car(dimensions)->integer)); - } - object *ptr = myalloc(); - ptr->type = ARRAY; - object *tree = nil; - if (size != 0) tree = buildarray(size, nextpower2(size), def); - ptr->cdr = cons(tree, dimensions); - return ptr; -} - -object **arrayref (object *array, int index, int size) { - int mask = nextpower2(size)>>1; - object **p = &car(cdr(array)); - while (mask) { - if ((index & mask) == 0) p = &(car(*p)); else p = &(cdr(*p)); - mask = mask>>1; - } - return p; -} - -object **getarray (object *array, object *subs, object *env, int *bit) { - int index = 0, size = 1, s; - *bit = -1; - bool bitp = false; - object *dims = cddr(array); - while (dims != NULL && subs != NULL) { - int d = car(dims)->integer; - if (d < 0) { d = -d; bitp = true; } - if (env) s = checkinteger(eval(car(subs), env)); else s = checkinteger(car(subs)); - if (s < 0 || s >= d) error("subscript out of range", car(subs)); - size = size * d; - index = index * d + s; - dims = cdr(dims); subs = cdr(subs); - } - if (dims != NULL) error2("too few subscripts"); - if (subs != NULL) error2("too many subscripts"); - if (bitp) { - size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - *bit = index & (sizeof(int)==4 ? 0x1F : 0x0F); - index = index>>(sizeof(int)==4 ? 5 : 4); - } - return arrayref(array, index, size); -} - -void rslice (object *array, int size, int slice, object *dims, object *args) { - int d = first(dims)->integer; - for (int i = 0; i < d; i++) { - int index = slice * d + i; - if (!consp(args)) error2("initial contents don't match array type"); - if (cdr(dims) == NULL) { - object **p = arrayref(array, index, size); - *p = car(args); - } else rslice(array, size, index, cdr(dims), car(args)); - args = cdr(args); - } -} - -object *readarray (int d, object *args) { - object *list = args; - object *dims = NULL; object *head = NULL; - int size = 1; - for (int i = 0; i < d; i++) { - if (!listp(list)) error2("initial contents don't match array type"); - int l = listlength(list); - if (dims == NULL) { dims = cons(number(l), NULL); head = dims; } - else { cdr(dims) = cons(number(l), NULL); dims = cdr(dims); } - size = size * l; - if (list != NULL) list = car(list); - } - object *array = makearray(head, NULL, false); - rslice(array, size, 0, head, args); - return array; -} - -object *readbitarray (gfun_t gfun) { - char ch = gfun(); - object *head = NULL; - object *tail = NULL; - while (!issp(ch) && !isbr(ch)) { - if (ch != '0' && ch != '1') error2("illegal character in bit array"); - object *cell = cons(number(ch - '0'), NULL); - if (head == NULL) head = cell; - else tail->cdr = cell; - tail = cell; - ch = gfun(); - } - LastChar = ch; - int size = listlength(head); - object *array = makearray(cons(number(size), NULL), number(0), true); - size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - int index = 0; - while (head != NULL) { - object **loc = arrayref(array, index>>(sizeof(int)==4 ? 5 : 4), size); - int bit = index & (sizeof(int)==4 ? 0x1F : 0x0F); - *loc = number((((*loc)->integer) & ~(1<integer)<integer; - if (d < 0) d = -d; - for (int i = 0; i < d; i++) { - if (i && spaces) pfun(' '); - int index = slice * d + i; - if (cdr(dims) == NULL) { - if (bitp) pint(((*arrayref(array, index>>(sizeof(int)==4 ? 5 : 4), size))->integer)>> - (index & (sizeof(int)==4 ? 0x1F : 0x0F)) & 1, pfun); - else printobject(*arrayref(array, index, size), pfun); - } else { pfun('('); pslice(array, size, index, cdr(dims), pfun, bitp); pfun(')'); } - } -} - -void printarray (object *array, pfun_t pfun) { - object *dimensions = cddr(array); - object *dims = dimensions; - bool bitp = false; - int size = 1, n = 0; - while (dims != NULL) { - int d = car(dims)->integer; - if (d < 0) { bitp = true; d = -d; } - size = size * d; - dims = cdr(dims); n++; - } - if (bitp) size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); - pfun('#'); - if (n == 1 && bitp) { pfun('*'); pslice(array, size, -1, dimensions, pfun, bitp); } - else { - if (n > 1) { pint(n, pfun); pfun('A'); } - pfun('('); pslice(array, size, 0, dimensions, pfun, bitp); pfun(')'); - } -} - -// String utilities - -void indent (uint8_t spaces, char ch, pfun_t pfun) { - for (uint8_t i=0; ichars & 0xFFFFFF) == 0) { - (*tail)->chars |= ch<<16; return; - } else if (((*tail)->chars & 0xFFFF) == 0) { - (*tail)->chars |= ch<<8; return; - } else if (((*tail)->chars & 0xFF) == 0) { - (*tail)->chars |= ch; return; - } else { - cell = myalloc(); car(*tail) = cell; - } - car(cell) = NULL; cell->chars = ch<<24; *tail = cell; -} - -object *copystring (object *arg) { - object *obj = newstring(); - object *ptr = obj; - arg = cdr(arg); - while (arg != NULL) { - object *cell = myalloc(); car(cell) = NULL; - if (cdr(obj) == NULL) cdr(obj) = cell; else car(ptr) = cell; - ptr = cell; - ptr->chars = arg->chars; - arg = car(arg); - } - return obj; -} - -object *readstring (uint8_t delim, bool esc, gfun_t gfun) { - object *obj = newstring(); - object *tail = obj; - int ch = gfun(); - if (ch == -1) return nil; - while ((ch != delim) && (ch != -1)) { - if (esc && ch == '\\') ch = gfun(); - buildstring(ch, &tail); - ch = gfun(); - } - return obj; -} - -int stringlength (object *form) { - int length = 0; - form = cdr(form); - while (form != NULL) { - int chars = form->chars; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - if (chars>>i & 0xFF) length++; - } - form = car(form); - } - return length; -} - -object **getcharplace (object *string, int n, int *shift) { - object **arg = &cdr(string); - int top; - if (sizeof(int) == 4) { top = n>>2; *shift = 3 - (n&3); } - else { top = n>>1; *shift = 1 - (n&1); } - *shift = - (*shift + 2); - for (int i=0; ichars)>>((-shift-2)<<3)) & 0xFF; -} - -int gstr () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - char c = nthchar(GlobalString, GlobalStringIndex++); - if (c != 0) return c; - return '\n'; // -1? -} - -void pstr (char c) { - buildstring(c, &GlobalStringTail); -} - -object *lispstring (char *s) { - object *obj = newstring(); - object *tail = obj; - while(1) { - char ch = *s++; - if (ch == 0) break; - if (ch == '\\') ch = *s++; - buildstring(ch, &tail); - } - return obj; -} - -int stringcompare (object *args, bool lt, bool gt, bool eq) { - object *arg1 = checkstring(first(args)); - object *arg2 = checkstring(second(args)); - arg1 = cdr(arg1); arg2 = cdr(arg2); - int m = 0; chars_t a = 0, b = 0; - while ((arg1 != NULL) || (arg2 != NULL)) { - if (arg1 == NULL) return lt ? m : -1; - if (arg2 == NULL) return gt ? m : -1; - a = arg1->chars; b = arg2->chars; - if (a < b) { if (lt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; } - if (a > b) { if (gt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; } - arg1 = car(arg1); arg2 = car(arg2); - m = m + sizeof(int); - } - if (eq) { m = m - sizeof(int); while (a != 0) { m++; a = a << 8;} return m;} else return -1; -} - -object *documentation (object *arg, object *env) { - if (arg == NULL) return nil; - if (!symbolp(arg)) error(notasymbol, arg); - object *pair = findpair(arg, env); - if (pair != NULL) { - object *val = cdr(pair); - if (listp(val) && first(val)->name == sym(LAMBDA) && cdr(val) != NULL && cddr(val) != NULL) { - if (stringp(third(val))) return third(val); - } - } - symbol_t docname = arg->name; - if (!builtinp(docname)) return nil; - char *docstring = lookupdoc(builtin(docname)); - if (docstring == NULL) return nil; - object *obj = startstring(); - pfstring(docstring, pstr); - return obj; -} - -object *apropos (object *arg, bool print) { - char buf[17], buf2[33]; - char *part = cstring(princtostring(arg), buf, 17); - object *result = cons(NULL, NULL); - object *ptr = result; - // User-defined? - object *globals = GlobalEnv; - while (globals != NULL) { - object *pair = first(globals); - object *var = car(pair); - object *val = cdr(pair); - char *full = cstring(princtostring(var), buf2, 33); - if (strstr(full, part) != NULL) { - if (print) { - printsymbol(var, pserial); pserial(' '); pserial('('); - if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) pfstring("user function", pserial); - else if (consp(val) && car(val)->type == CODE) pfstring("code", pserial); - else pfstring("user symbol", pserial); - pserial(')'); pln(pserial); - } else { - cdr(ptr) = cons(var, NULL); ptr = cdr(ptr); - } - } - globals = cdr(globals); - testescape(); - } - // Built-in? - int entries = tablesize(0) + tablesize(1); - for (int i = 0; i < entries; i++) { - if (findsubstring(part, (builtin_t)i)) { - if (print) { - uint8_t fntype = getminmax(i)>>6; - pbuiltin((builtin_t)i, pserial); pserial(' '); pserial('('); - if (fntype == FUNCTIONS) pfstring("function", pserial); - else if (fntype == SPECIAL_FORMS) pfstring("special form", pserial); - else pfstring("symbol/keyword", pserial); - pserial(')'); pln(pserial); - } else { - cdr(ptr) = cons(bsymbol(i), NULL); ptr = cdr(ptr); - } - } - testescape(); - } - return cdr(result); -} - -char *cstring (object *form, char *buffer, int buflen) { - form = cdr(checkstring(form)); - int index = 0; - while (form != NULL) { - int chars = form->integer; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - char ch = chars>>i & 0xFF; - if (ch) { - if (index >= buflen-1) error2("no room for string"); - buffer[index++] = ch; - } - } - form = car(form); - } - buffer[index] = '\0'; - return buffer; -} - -object *iptostring (uint32_t ip) { - union { uint32_t data2; uint8_t u8[4]; }; - object *obj = startstring(); - data2 = ip; - for (int i=0; i<4; i++) { - if (i) pstr('.'); - pintbase(u8[i], 10, pstr); - } - return obj; -} - -uint32_t ipstring (object *form) { - form = cdr(checkstring(form)); - int p = 0; - union { uint32_t ipaddress; uint8_t ipbytes[4]; } ; - ipaddress = 0; - while (form != NULL) { - int chars = form->integer; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - char ch = chars>>i & 0xFF; - if (ch) { - if (ch == '.') { p++; if (p > 3) error2("illegal IP address"); } - else ipbytes[p] = (ipbytes[p] * 10) + ch - '0'; - } - } - form = car(form); - } - return ipaddress; -} - -// Lookup variable in environment - -object *value (symbol_t n, object *env) { - while (env != NULL) { - object *pair = car(env); - if (pair != NULL && car(pair)->name == n) return pair; - env = cdr(env); - } - return nil; -} - -object *findpair (object *var, object *env) { - symbol_t name = var->name; - object *pair = value(name, env); - if (pair == NULL) pair = value(name, GlobalEnv); - return pair; -} - -bool boundp (object *var, object *env) { - if (!symbolp(var)) error(notasymbol, var); - return (findpair(var, env) != NULL); -} - -object *findvalue (object *var, object *env) { - object *pair = findpair(var, env); - if (pair == NULL) error("unknown variable", var); - return pair; -} - -// Handling closures - -object *closure (int tc, symbol_t name, object *function, object *args, object **env) { - object *state = car(function); - function = cdr(function); - int trace = 0; - if (name) trace = tracing(name); - if (trace) { - indent(TraceDepth[trace-1]<<1, ' ', pserial); - pint(TraceDepth[trace-1]++, pserial); - pserial(':'); pserial(' '); pserial('('); printsymbol(symbol(name), pserial); - } - object *params = first(function); - if (!listp(params)) errorsym(name, notalist, params); - function = cdr(function); - // Dropframe - if (tc) { - if (*env != NULL && car(*env) == NULL) { - pop(*env); - while (*env != NULL && car(*env) != NULL) pop(*env); - } else push(nil, *env); - } - // Push state - while (consp(state)) { - object *pair = first(state); - push(pair, *env); - state = cdr(state); - } - // Add arguments to environment - bool optional = false; - while (params != NULL) { - object *value; - object *var = first(params); - if (isbuiltin(var, OPTIONAL)) optional = true; - else { - if (consp(var)) { - if (!optional) errorsym(name, "invalid default value", var); - if (args == NULL) value = eval(second(var), *env); - else { value = first(args); args = cdr(args); } - var = first(var); - if (!symbolp(var)) errorsym(name, "illegal optional parameter", var); - } else if (!symbolp(var)) { - errorsym(name, "illegal function parameter", var); - } else if (isbuiltin(var, AMPREST)) { - params = cdr(params); - var = first(params); - value = args; - args = NULL; - } else { - if (args == NULL) { - if (optional) value = nil; - else errorsym2(name, toofewargs); - } else { value = first(args); args = cdr(args); } - } - push(cons(var,value), *env); - if (trace) { pserial(' '); printobject(value, pserial); } - } - params = cdr(params); - } - if (args != NULL) errorsym2(name, toomanyargs); - if (trace) { pserial(')'); pln(pserial); } - // Do an implicit progn - if (tc) push(nil, *env); - return tf_progn(function, *env); -} - -object *apply (object *function, object *args, object *env) { - if (symbolp(function)) { - builtin_t fname = builtin(function->name); - if ((fname < ENDFUNCTIONS) && ((getminmax(fname)>>6) == FUNCTIONS)) { - Context = fname; - checkargs(args); - return ((fn_ptr_type)lookupfn(fname))(args, env); - } else function = eval(function, env); - } - if (consp(function) && isbuiltin(car(function), LAMBDA)) { - object *result = closure(0, sym(NIL), function, args, &env); - return eval(result, env); - } - if (consp(function) && isbuiltin(car(function), CLOSURE)) { - function = cdr(function); - object *result = closure(0, sym(NIL), function, args, &env); - return eval(result, env); - } - error("illegal function", function); - return NULL; -} - -// In-place operations - -object **place (object *args, object *env, int *bit) { - *bit = -1; - if (atom(args)) return &cdr(findvalue(args, env)); - object* function = first(args); - if (symbolp(function)) { - symbol_t sname = function->name; - if (sname == sym(CAR) || sname == sym(FIRST)) { - object *value = eval(second(args), env); - if (!listp(value)) error(canttakecar, value); - return &car(value); - } - if (sname == sym(CDR) || sname == sym(REST)) { - object *value = eval(second(args), env); - if (!listp(value)) error(canttakecdr, value); - return &cdr(value); - } - if (sname == sym(NTH)) { - int index = checkinteger(eval(second(args), env)); - object *list = eval(third(args), env); - if (atom(list)) { Context = NTH; error("second argument is not a list", list); } - int i = index; - while (i > 0) { - list = cdr(list); - if (list == NULL) { Context = NTH; error(indexrange, number(index)); } - i--; - } - return &car(list); - } - if (sname == sym(CHAR)) { - int index = checkinteger(eval(third(args), env)); - object *string = checkstring(eval(second(args), env)); - object **loc = getcharplace(string, index, bit); - if ((*loc) == NULL || (((((*loc)->chars)>>((-(*bit)-2)<<3)) & 0xFF) == 0)) { Context = CHAR; error(indexrange, number(index)); } - return loc; - } - if (sname == sym(AREF)) { - object *array = eval(second(args), env); - if (!arrayp(array)) { Context = AREF; error("first argument is not an array", array); } - return getarray(array, cddr(args), env, bit); - } - } - error2("illegal place"); - return nil; -} - -// Checked car and cdr - -object *carx (object *arg) { - if (!listp(arg)) error(canttakecar, arg); - if (arg == nil) return nil; - return car(arg); -} - -object *cdrx (object *arg) { - if (!listp(arg)) error(canttakecdr, arg); - if (arg == nil) return nil; - return cdr(arg); -} - -object *cxxxr (object *args, uint8_t pattern) { - object *arg = first(args); - while (pattern != 1) { - if ((pattern & 1) == 0) arg = carx(arg); else arg = cdrx(arg); - pattern = pattern>>1; - } - return arg; -} - -// Mapping helper functions - -object *mapcl (object *args, object *env, bool mapl) { - object *function = first(args); - args = cdr(args); - object *result = first(args); - protect(result); - object *params = cons(NULL, NULL); - protect(params); - // Make parameters - while (true) { - object *tailp = params; - object *lists = args; - while (lists != NULL) { - object *list = car(lists); - if (list == NULL) { - unprotect(); unprotect(); - return result; - } - if (improperp(list)) error(notproper, list); - object *item = mapl ? list : first(list); - object *obj = cons(item, NULL); - car(lists) = cdr(list); - cdr(tailp) = obj; tailp = obj; - lists = cdr(lists); - } - apply(function, cdr(params), env); - } -} - -void mapcarfun (object *result, object **tail) { - object *obj = cons(result,NULL); - cdr(*tail) = obj; *tail = obj; -} - -void mapcanfun (object *result, object **tail) { - if (cdr(*tail) != NULL) error(notproper, *tail); - while (consp(result)) { - cdr(*tail) = result; *tail = result; - result = cdr(result); - } -} - -object *mapcarcan (object *args, object *env, mapfun_t fun, bool maplist) { - object *function = first(args); - args = cdr(args); - object *params = cons(NULL, NULL); - protect(params); - object *head = cons(NULL, NULL); - protect(head); - object *tail = head; - // Make parameters - while (true) { - object *tailp = params; - object *lists = args; - while (lists != NULL) { - object *list = car(lists); - if (list == NULL) { - unprotect(); unprotect(); - return cdr(head); - } - if (improperp(list)) error(notproper, list); - object *item = maplist ? list : first(list); - object *obj = cons(item, NULL); - car(lists) = cdr(list); - cdr(tailp) = obj; tailp = obj; - lists = cdr(lists); - } - object *result = apply(function, cdr(params), env); - fun(result, &tail); - } -} - -object *dobody (object *args, object *env, bool star) { - object *varlist = first(args), *endlist = second(args); - object *head = cons(NULL, NULL); - protect(head); - object *ptr = head; - object *newenv = env; - while (varlist != NULL) { - object *varform = first(varlist); - object *var, *init = NULL, *step = NULL; - if (atom(varform)) var = varform; - else { - var = first(varform); - varform = cdr(varform); - if (varform != NULL) { - init = eval(first(varform), env); - varform = cdr(varform); - if (varform != NULL) step = cons(first(varform), NULL); - } - } - object *pair = cons(var, init); - push(pair, newenv); - if (star) env = newenv; - object *cell = cons(cons(step, pair), NULL); - cdr(ptr) = cell; ptr = cdr(ptr); - varlist = cdr(varlist); - } - env = newenv; - head = cdr(head); - object *endtest = first(endlist), *results = cdr(endlist); - while (eval(endtest, env) == NULL) { - object *forms = cddr(args); - while (forms != NULL) { - object *result = eval(car(forms), env); - if (tstflag(RETURNFLAG)) { - clrflag(RETURNFLAG); - return result; - } - forms = cdr(forms); - } - object *varlist = head; - int count = 0; - while (varlist != NULL) { - object *varform = first(varlist); - object *step = car(varform), *pair = cdr(varform); - if (step != NULL) { - object *val = eval(first(step), env); - if (star) { - cdr(pair) = val; - } else { - push(val, GCStack); - push(pair, GCStack); - count++; - } - } - varlist = cdr(varlist); - } - while (count > 0) { - cdr(car(GCStack)) = car(cdr(GCStack)); - pop(GCStack); pop(GCStack); - count--; - } - } - unprotect(); - return eval(tf_progn(results, env), env); -} - -// I2C interface for up to two ports, using Arduino Wire - -void I2Cinit (TwoWire *port, bool enablePullup) { - (void) enablePullup; - port->begin(); -} - -int I2Cread (TwoWire *port) { - return port->read(); -} - -void I2Cwrite (TwoWire *port, uint8_t data) { - port->write(data); -} - -bool I2Cstart (TwoWire *port, uint8_t address, uint8_t read) { - int ok = true; - if (read == 0) { - port->beginTransmission(address); - ok = (port->endTransmission(true) == 0); - port->beginTransmission(address); - } - else port->requestFrom(address, I2Ccount); - return ok; -} - -bool I2Crestart (TwoWire *port, uint8_t address, uint8_t read) { - int error = (port->endTransmission(false) != 0); - if (read == 0) port->beginTransmission(address); - else port->requestFrom(address, I2Ccount); - return error ? false : true; -} - -void I2Cstop (TwoWire *port, uint8_t read) { - if (read == 0) port->endTransmission(); // Check for error? - // Release pins - port->end(); -} - -// Streams - -// Simplify board differences -#if defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) -#define ULISP_I2C1 -#endif - -inline int spiread () { return SPI.transfer(0); } -inline int i2cread () { return I2Cread(&Wire); } -#if defined(ULISP_I2C1) -inline int i2c1read () { return I2Cread(&Wire1); } -#endif -inline int serial1read () { while (!Serial1.available()) testescape(); return Serial1.read(); } -#if defined(sdcardsupport) -File SDpfile, SDgfile; -inline int SDread () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - return SDgfile.read(); -} -#endif - -WiFiClient client; -WiFiServer server(80); - -inline int WiFiread () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - while (!client.available()) testescape(); - return client.read(); -} - -void serialbegin (int address, int baud) { - if (address == 1) Serial1.begin((long)baud*100); - else error("port not supported", number(address)); -} - -void serialend (int address) { - if (address == 1) {Serial1.flush(); Serial1.end(); } - else error("port not supported", number(address)); -} - -gfun_t gstreamfun (object *args) { - int streamtype = SERIALSTREAM; - int address = 0; - gfun_t gfun = gserial; - if (args != NULL) { - int stream = isstream(first(args)); - streamtype = stream>>8; address = stream & 0xFF; - } - if (streamtype == I2CSTREAM) { - if (address < 128) gfun = i2cread; - #if defined(ULISP_I2C1) - else gfun = i2c1read; - #endif - } else if (streamtype == SPISTREAM) gfun = spiread; - else if (streamtype == SERIALSTREAM) { - if (address == 0) gfun = gserial; - else if (address == 1) gfun = serial1read; - } - #if defined(sdcardsupport) - else if (streamtype == SDSTREAM) gfun = (gfun_t)SDread; - #endif - else if (streamtype == WIFISTREAM) gfun = (gfun_t)WiFiread; - else error2("unknown stream type"); - return gfun; -} - -inline void spiwrite (char c) { SPI.transfer(c); } -inline void i2cwrite (char c) { I2Cwrite(&Wire, c); } -#if defined(ULISP_I2C1) -inline void i2c1write (char c) { I2Cwrite(&Wire1, c); } -#endif -inline void serial1write (char c) { Serial1.write(c); } -inline void WiFiwrite (char c) { client.write(c); } -#if defined(sdcardsupport) -inline void SDwrite (char c) { SDpfile.write(c); } -#endif -#if defined(gfxsupport) -inline void gfxwrite (char c) { tft.write(c); } -#endif - -pfun_t pstreamfun (object *args) { - int streamtype = SERIALSTREAM; - int address = 0; - pfun_t pfun = pserial; - if (args != NULL && first(args) != NULL) { - int stream = isstream(first(args)); - streamtype = stream>>8; address = stream & 0xFF; - } - if (streamtype == I2CSTREAM) { - if (address < 128) pfun = i2cwrite; - #if defined(ULISP_I2C1) - else pfun = i2c1write; - #endif - } else if (streamtype == SPISTREAM) pfun = spiwrite; - else if (streamtype == SERIALSTREAM) { - if (address == 0) pfun = pserial; - else if (address == 1) pfun = serial1write; - } - else if (streamtype == STRINGSTREAM) { - pfun = pstr; - } - #if defined(sdcardsupport) - else if (streamtype == SDSTREAM) pfun = (pfun_t)SDwrite; - #endif - #if defined(gfxsupport) - else if (streamtype == GFXSTREAM) pfun = (pfun_t)gfxwrite; - #endif - else if (streamtype == WIFISTREAM) pfun = (pfun_t)WiFiwrite; - else error2("unknown stream type"); - return pfun; -} - -// Check pins - -void checkanalogread (int pin) { -#if defined(ESP32) || defined(ARDUINO_ESP32_DEV) - if (!(pin==0 || pin==2 || pin==4 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=36) || pin==39)) - error("invalid pin", number(pin)); -#elif defined(ARDUINO_FEATHER_ESP32) - if (!(pin==4 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=36) || pin==39)) - error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) - if (!(pin==8 || (pin>=14 && pin<=18))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) - if (!(pin==4 || pin==7 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=33))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) - if (!((pin>=5 && pin<=9) || (pin>=16 && pin<=18))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) - if (!((pin>=0 && pin<=1) || (pin>=3 && pin<=5))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_FEATHERS2) | defined(ARDUINO_ESP32S2_DEV) - if (!((pin>=1 && pin<=20))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ESP32C3_DEV) - if (!((pin>=0 && pin<=5))) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ESP32S3_DEV) - if (!((pin>=1 && pin<=20))) error("invalid pin", number(pin)); -#endif -} - -void checkanalogwrite (int pin) { -#if defined(ESP32) || defined(ARDUINO_FEATHER_ESP32) || defined(ARDUINO_ESP32_DEV) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) - if (!(pin>=25 && pin<=26)) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) || defined(ARDUINO_FEATHERS2) || defined(ARDUINO_ESP32S2_DEV) - if (!(pin>=17 && pin<=18)) error("invalid pin", number(pin)); -#elif defined(ARDUINO_ESP32C3_DEV) | defined(ARDUINO_ESP32S3_DEV) | defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) - error2(ANALOGWRITE, "not supported"); -#endif -} - -// Note - -void tone (int pin, int note) { - (void) pin, (void) note; -} - -void noTone (int pin) { - (void) pin; -} - -const int scale[] = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902}; - -void playnote (int pin, int note, int octave) { - int oct = octave + note/12; - int prescaler = 8 - oct; - if (prescaler<0 || prescaler>8) error("octave out of range", number(oct)); - tone(pin, scale[note%12]>>prescaler); -} - -void nonote (int pin) { - noTone(pin); -} - -// Sleep - -void initsleep () { } - -void doze (int secs) { - delay(1000 * secs); -} - -// Prettyprint - -const int PPINDENT = 2; -const int PPWIDTH = 80; -const int GFXPPWIDTH = 52; // 320 pixel wide screen -int ppwidth = PPWIDTH; - -void pcount (char c) { - if (c == '\n') PrintCount++; - PrintCount++; -} - -uint8_t atomwidth (object *obj) { - PrintCount = 0; - printobject(obj, pcount); - return PrintCount; -} - -uint8_t basewidth (object *obj, uint8_t base) { - PrintCount = 0; - pintbase(obj->integer, base, pcount); - return PrintCount; -} - -bool quoted (object *obj) { - return (consp(obj) && car(obj) != NULL && car(obj)->name == sym(QUOTE) && consp(cdr(obj)) && cddr(obj) == NULL); -} - -int subwidth (object *obj, int w) { - if (atom(obj)) return w - atomwidth(obj); - if (quoted(obj)) obj = car(cdr(obj)); - return subwidthlist(obj, w - 1); -} - -int subwidthlist (object *form, int w) { - while (form != NULL && w >= 0) { - if (atom(form)) return w - (2 + atomwidth(form)); - w = subwidth(car(form), w - 1); - form = cdr(form); - } - return w; -} - -void superprint (object *form, int lm, pfun_t pfun) { - if (atom(form)) { - if (symbolp(form) && form->name == sym(NOTHING)) printsymbol(form, pfun); - else printobject(form, pfun); - } else if (quoted(form)) { - pfun('\''); - superprint(car(cdr(form)), lm + 1, pfun); - } else { - lm = lm + PPINDENT; - bool fits = (subwidth(form, ppwidth - lm - PPINDENT) >= 0); - int special = 0, extra = 0; bool separate = true; - object *arg = car(form); - if (symbolp(arg) && builtinp(arg->name)) { - uint8_t minmax = getminmax(builtin(arg->name)); - if (minmax == 0327 || minmax == 0313) special = 2; // defun, setq, setf, defvar - else if (minmax == 0317 || minmax == 0017 || minmax == 0117 || minmax == 0123) special = 1; - } - while (form != NULL) { - if (atom(form)) { pfstring(" . ", pfun); printobject(form, pfun); pfun(')'); return; } - else if (separate) { - pfun('('); - separate = false; - } else if (special) { - pfun(' '); - special--; - } else if (fits) { - pfun(' '); - } else { pln(pfun); indent(lm, ' ', pfun); } - superprint(car(form), lm+extra, pfun); - form = cdr(form); - } - pfun(')'); - } -} - -object *edit (object *fun) { - while (1) { - if (tstflag(EXITEDITOR)) return fun; - char c = gserial(); - if (c == 'q') setflag(EXITEDITOR); - else if (c == 'b') return fun; - else if (c == 'r') fun = read(gserial); - else if (c == '\n') { pfl(pserial); superprint(fun, 0, pserial); pln(pserial); } - else if (c == 'c') fun = cons(read(gserial), fun); - else if (atom(fun)) pserial('!'); - else if (c == 'd') fun = cons(car(fun), edit(cdr(fun))); - else if (c == 'a') fun = cons(edit(car(fun)), cdr(fun)); - else if (c == 'x') fun = cdr(fun); - else pserial('?'); - } -} - -// Special forms - -object *sp_quote (object *args, object *env) { - (void) env; - return first(args); -} - -object *sp_or (object *args, object *env) { - while (args != NULL) { - object *val = eval(car(args), env); - if (val != NULL) return val; - args = cdr(args); - } - return nil; -} - -object *sp_defun (object *args, object *env) { - (void) env; - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - object *val = cons(bsymbol(LAMBDA), cdr(args)); - object *pair = value(var->name, GlobalEnv); - if (pair != NULL) cdr(pair) = val; - else push(cons(var, val), GlobalEnv); - return var; -} - -object *sp_defvar (object *args, object *env) { - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - object *val = NULL; - args = cdr(args); - if (args != NULL) { setflag(NOESC); val = eval(first(args), env); clrflag(NOESC); } - object *pair = value(var->name, GlobalEnv); - if (pair != NULL) cdr(pair) = val; - else push(cons(var, val), GlobalEnv); - return var; -} - -object *sp_setq (object *args, object *env) { - object *arg = nil; - while (args != NULL) { - if (cdr(args) == NULL) error2(oddargs); - object *pair = findvalue(first(args), env); - arg = eval(second(args), env); - cdr(pair) = arg; - args = cddr(args); - } - return arg; -} - -object *sp_loop (object *args, object *env) { - object *start = args; - for (;;) { - yield(); - args = start; - while (args != NULL) { - object *result = eval(car(args),env); - if (tstflag(RETURNFLAG)) { - clrflag(RETURNFLAG); - return result; - } - args = cdr(args); - } - testescape(); - } -} - -object *sp_push (object *args, object *env) { - int bit; - object *item = eval(first(args), env); - object **loc = place(second(args), env, &bit); - if (bit != -1) error2(invalidarg); - push(item, *loc); - return *loc; -} - -object *sp_pop (object *args, object *env) { - int bit; - object *arg = first(args); - if (arg == NULL) error2(invalidarg); - object **loc = place(arg, env, &bit); - if (bit < -1) error(invalidarg, arg); - if (!consp(*loc)) error(notalist, *loc); - object *result = car(*loc); - pop(*loc); - return result; -} - -// Accessors - -object *sp_incf (object *args, object *env) { - int bit; - object **loc = place(first(args), env, &bit); - if (bit < -1) error2(notanumber); - args = cdr(args); - - object *x = *loc; - object *inc = (args != NULL) ? eval(first(args), env) : NULL; - - if (bit != -1) { - int increment; - if (inc == NULL) increment = 1; else increment = checkbitvalue(inc); - int newvalue = (((*loc)->integer)>>bit & 1) + increment; - - if (newvalue & ~1) error2("result is not a bit value"); - *loc = number((((*loc)->integer) & ~(1<integer; - - if (inc == NULL) increment = 1; else increment = inc->integer; - - if (increment < 1) { - if (INT_MIN - increment > value) *loc = makefloat((float)value + (float)increment); - else *loc = number(value + increment); - } else { - if (INT_MAX - increment < value) *loc = makefloat((float)value + (float)increment); - else *loc = number(value + increment); - } - } else error2(notanumber); - return *loc; -} - -object *sp_decf (object *args, object *env) { - int bit; - object **loc = place(first(args), env, &bit); - if (bit < -1) error2(notanumber); - args = cdr(args); - - object *x = *loc; - object *dec = (args != NULL) ? eval(first(args), env) : NULL; - - if (bit != -1) { - int decrement; - if (dec == NULL) decrement = 1; else decrement = checkbitvalue(dec); - int newvalue = (((*loc)->integer)>>bit & 1) - decrement; - - if (newvalue & ~1) error2("result is not a bit value"); - *loc = number((((*loc)->integer) & ~(1<integer; - - if (dec == NULL) decrement = 1; else decrement = dec->integer; - - if (decrement < 1) { - if (INT_MAX + decrement < value) *loc = makefloat((float)value - (float)decrement); - else *loc = number(value - decrement); - } else { - if (INT_MIN + decrement > value) *loc = makefloat((float)value - (float)decrement); - else *loc = number(value - decrement); - } - } else error2(notanumber); - return *loc; -} - -object *sp_setf (object *args, object *env) { - int bit; - object *arg = nil; - while (args != NULL) { - if (cdr(args) == NULL) error2(oddargs); - object **loc = place(first(args), env, &bit); - arg = eval(second(args), env); - if (bit == -1) *loc = arg; - else if (bit < -1) (*loc)->chars = ((*loc)->chars & ~(0xff<<((-bit-2)<<3))) | checkchar(arg)<<((-bit-2)<<3); - else *loc = number((checkinteger(*loc) & ~(1<name); - args = cdr(args); - } - int i = 0; - while (i < TRACEMAX) { - if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args); - i++; - } - return args; -} - -object *sp_untrace (object *args, object *env) { - (void) env; - if (args == NULL) { - int i = 0; - while (i < TRACEMAX) { - if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args); - TraceFn[i] = 0; - i++; - } - } else { - while (args != NULL) { - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - untrace(var->name); - args = cdr(args); - } - } - return args; -} - -object *sp_formillis (object *args, object *env) { - object *param = checkarguments(args, 0, 1); - unsigned long start = millis(); - unsigned long now, total = 0; - if (param != NULL) total = checkinteger(eval(first(param), env)); - eval(tf_progn(cdr(args),env), env); - do { - now = millis() - start; - testescape(); - } while (now < total); - if (now <= INT_MAX) return number(now); - return nil; -} - -object *sp_time (object *args, object *env) { - unsigned long start = millis(); - object *result = eval(first(args), env); - unsigned long elapsed = millis() - start; - printobject(result, pserial); - pfstring("\nTime: ", pserial); - if (elapsed < 1000) { - pint(elapsed, pserial); - pfstring(" ms\n", pserial); - } else { - elapsed = elapsed+50; - pint(elapsed/1000, pserial); - pserial('.'); pint((elapsed/100)%10, pserial); - pfstring(" s\n", pserial); - } - return bsymbol(NOTHING); -} - -object *sp_withoutputtostring (object *args, object *env) { - object *params = checkarguments(args, 1, 1); - object *var = first(params); - object *pair = cons(var, stream(STRINGSTREAM, 0)); - push(pair,env); - object *string = startstring(); - protect(string); - object *forms = cdr(args); - eval(tf_progn(forms,env), env); - unprotect(); - return string; -} - -object *sp_withserial (object *args, object *env) { - object *params = checkarguments(args, 2, 3); - object *var = first(params); - int address = checkinteger(eval(second(params), env)); - params = cddr(params); - int baud = 96; - if (params != NULL) baud = checkinteger(eval(first(params), env)); - object *pair = cons(var, stream(SERIALSTREAM, address)); - push(pair,env); - serialbegin(address, baud); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - serialend(address); - return result; -} - -object *sp_withi2c (object *args, object *env) { - object *params = checkarguments(args, 2, 4); - object *var = first(params); - int address = checkinteger(eval(second(params), env)); - params = cddr(params); - if ((address == 0 || address == 1) && params != NULL) { - address = address * 128 + checkinteger(eval(first(params), env)); - params = cdr(params); - } - int read = 0; // Write - I2Ccount = 0; - if (params != NULL) { - object *rw = eval(first(params), env); - if (integerp(rw)) I2Ccount = rw->integer; - read = (rw != NULL); - } - // Top bit of address is I2C port - TwoWire *port = &Wire; - #if defined(ULISP_I2C1) - if (address > 127) port = &Wire1; - #endif - I2Cinit(port, 1); // Pullups - object *pair = cons(var, (I2Cstart(port, address & 0x7F, read)) ? stream(I2CSTREAM, address) : nil); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - I2Cstop(port, read); - return result; -} - -object *sp_withsdcard (object *args, object *env) { - #if defined(sdcardsupport) - object *params = checkarguments(args, 2, 3); - object *var = first(params); - params = cdr(params); - if (params == NULL) error2("no filename specified"); - builtin_t temp = Context; - object *filename = eval(first(params), env); - Context = temp; - if (!stringp(filename)) error("filename is not a string", filename); - params = cdr(params); - SD.begin(); - int mode = 0; - if (params != NULL && first(params) != NULL) mode = checkinteger(first(params)); - const char *oflag = FILE_READ; - if (mode == 1) oflag = FILE_APPEND; else if (mode == 2) oflag = FILE_WRITE; - if (mode >= 1) { - char buffer[BUFFERSIZE]; - SDpfile = SD.open(MakeFilename(filename, buffer), oflag); - if (!SDpfile) error2("problem writing to SD card or invalid filename"); - } else { - char buffer[BUFFERSIZE]; - SDgfile = SD.open(MakeFilename(filename, buffer), oflag); - if (!SDgfile) error2("problem reading from SD card or invalid filename"); - } - object *pair = cons(var, stream(SDSTREAM, 1)); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - if (mode >= 1) SDpfile.close(); else SDgfile.close(); - return result; - #else - (void) args, (void) env; - error2("not supported"); - return nil; - #endif -} - -// Tail-recursive forms - -object *tf_progn (object *args, object *env) { - if (args == NULL) return nil; - object *more = cdr(args); - while (more != NULL) { - object *result = eval(car(args),env); - if (tstflag(RETURNFLAG)) return quote(result); - args = more; - more = cdr(args); - } - return car(args); -} - -object *tf_if (object *args, object *env) { - if (args == NULL || cdr(args) == NULL) error2(toofewargs); - if (eval(first(args), env) != nil) return second(args); - args = cddr(args); - return (args != NULL) ? first(args) : nil; -} - -object *tf_cond (object *args, object *env) { - while (args != NULL) { - object *clause = first(args); - if (!consp(clause)) error(illegalclause, clause); - object *test = eval(first(clause), env); - object *forms = cdr(clause); - if (test != nil) { - if (forms == NULL) return quote(test); else return tf_progn(forms, env); - } - args = cdr(args); - } - return nil; -} - -object *tf_when (object *args, object *env) { - if (args == NULL) error2(noargument); - if (eval(first(args), env) != nil) return tf_progn(cdr(args),env); - else return nil; -} - -object *tf_unless (object *args, object *env) { - if (args == NULL) error2(noargument); - if (eval(first(args), env) != nil) return nil; - else return tf_progn(cdr(args),env); -} - -object *tf_case (object *args, object *env) { - object *test = eval(first(args), env); - args = cdr(args); - while (args != NULL) { - object *clause = first(args); - if (!consp(clause)) error(illegalclause, clause); - object *key = car(clause); - object *forms = cdr(clause); - if (consp(key)) { - while (key != NULL) { - if (eq(test,car(key))) return tf_progn(forms, env); - key = cdr(key); - } - } else if (eq(test,key) || eq(key,tee)) return tf_progn(forms, env); - args = cdr(args); - } - return nil; -} - -object *tf_and (object *args, object *env) { - if (args == NULL) return tee; - object *more = cdr(args); - while (more != NULL) { - if (eval(car(args), env) == NULL) return nil; - args = more; - more = cdr(args); - } - return car(args); -} - -// Core functions - -object *fn_not (object *args, object *env) { - (void) env; - return (first(args) == nil) ? tee : nil; -} - -object *fn_cons (object *args, object *env) { - (void) env; - return cons(first(args), second(args)); -} - -object *fn_atom (object *args, object *env) { - (void) env; - return atom(first(args)) ? tee : nil; -} - -object *fn_listp (object *args, object *env) { - (void) env; - return listp(first(args)) ? tee : nil; -} - -object *fn_consp (object *args, object *env) { - (void) env; - return consp(first(args)) ? tee : nil; -} - -object *fn_symbolp (object *args, object *env) { - (void) env; - object *arg = first(args); - return (arg == NULL || symbolp(arg)) ? tee : nil; -} - -object *fn_arrayp (object *args, object *env) { - (void) env; - return arrayp(first(args)) ? tee : nil; -} - -object *fn_boundp (object *args, object *env) { - return boundp(first(args), env) ? tee : nil; -} - -object *fn_keywordp (object *args, object *env) { - (void) env; - object *arg = first(args); - if (!symbolp(arg)) return nil; - return (keywordp(arg) || colonp(arg->name)) ? tee : nil; -} - -object *fn_setfn (object *args, object *env) { - object *arg = nil; - while (args != NULL) { - if (cdr(args) == NULL) error2(oddargs); - object *pair = findvalue(first(args), env); - arg = second(args); - cdr(pair) = arg; - args = cddr(args); - } - return arg; -} - -object *fn_streamp (object *args, object *env) { - (void) env; - object *arg = first(args); - return streamp(arg) ? tee : nil; -} - -object *fn_eq (object *args, object *env) { - (void) env; - return eq(first(args), second(args)) ? tee : nil; -} - -object *fn_equal (object *args, object *env) { - (void) env; - return equal(first(args), second(args)) ? tee : nil; -} - -// List functions - -object *fn_car (object *args, object *env) { - (void) env; - return carx(first(args)); -} - -object *fn_cdr (object *args, object *env) { - (void) env; - return cdrx(first(args)); -} - -object *fn_caar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b100); -} - -object *fn_cadr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b101); -} - -object *fn_cdar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b110); -} - -object *fn_cddr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b111); -} - -object *fn_caaar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1000); -} - -object *fn_caadr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1001);; -} - -object *fn_cadar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1010); -} - -object *fn_caddr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1011); -} - -object *fn_cdaar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1100); -} - -object *fn_cdadr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1101); -} - -object *fn_cddar (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1110); -} - -object *fn_cdddr (object *args, object *env) { - (void) env; - return cxxxr(args, 0b1111); -} - -object *fn_length (object *args, object *env) { - (void) env; - object *arg = first(args); - if (listp(arg)) return number(listlength(arg)); - if (stringp(arg)) return number(stringlength(arg)); - if (!(arrayp(arg) && cdr(cddr(arg)) == NULL)) error("argument is not a list, 1d array, or string", arg); - return number(abs(first(cddr(arg))->integer)); -} - -object *fn_arraydimensions (object *args, object *env) { - (void) env; - object *array = first(args); - if (!arrayp(array)) error("argument is not an array", array); - object *dimensions = cddr(array); - return (first(dimensions)->integer < 0) ? cons(number(-(first(dimensions)->integer)), cdr(dimensions)) : dimensions; -} - -object *fn_list (object *args, object *env) { - (void) env; - return args; -} - -object *fn_copylist (object *args, object *env) { - (void) env; - object *arg = first(args); - if (!listp(arg)) error(notalist, arg); - object *result = cons(NULL, NULL); - object *ptr = result; - while (arg != NULL) { - cdr(ptr) = cons(car(arg), NULL); - ptr = cdr(ptr); arg = cdr(arg); - } - return cdr(result); -} - -object *fn_makearray (object *args, object *env) { - (void) env; - object *def = nil; - bool bitp = false; - object *dims = first(args); - if (dims == NULL) error2("dimensions can't be nil"); - else if (atom(dims)) dims = cons(dims, NULL); - args = cdr(args); - while (args != NULL && cdr(args) != NULL) { - object *var = first(args); - if (isbuiltin(first(args), INITIALELEMENT)) def = second(args); - else if (isbuiltin(first(args), ELEMENTTYPE) && isbuiltin(second(args), BIT)) bitp = true; - else error("argument not recognised", var); - args = cddr(args); - } - if (bitp) { - if (def == nil) def = number(0); - else def = number(-checkbitvalue(def)); // 1 becomes all ones - } - return makearray(dims, def, bitp); -} - -object *fn_reverse (object *args, object *env) { - (void) env; - object *list = first(args); - object *result = NULL; - while (list != NULL) { - if (improperp(list)) error(notproper, list); - push(first(list),result); - list = cdr(list); - } - return result; -} - -object *fn_nth (object *args, object *env) { - (void) env; - int n = checkinteger(first(args)); - if (n < 0) error(indexnegative, first(args)); - object *list = second(args); - while (list != NULL) { - if (improperp(list)) error(notproper, list); - if (n == 0) return car(list); - list = cdr(list); - n--; - } - return nil; -} - -object *fn_aref (object *args, object *env) { - (void) env; - int bit; - object *array = first(args); - if (!arrayp(array)) error("first argument is not an array", array); - object *loc = *getarray(array, cdr(args), 0, &bit); - if (bit == -1) return loc; - else return number((loc->integer)>>bit & 1); -} - -object *fn_assoc (object *args, object *env) { - (void) env; - object *key = first(args); - object *list = second(args); - object *test = testargument(cddr(args)); - while (list != NULL) { - if (improperp(list)) error(notproper, list); - object *pair = first(list); - if (!listp(pair)) error("element is not a list", pair); - if (pair != NULL && apply(test, cons(key, cons(car(pair), NULL)), env) != NULL) return pair; - list = cdr(list); - } - return nil; -} - -object *fn_member (object *args, object *env) { - (void) env; - object *item = first(args); - object *list = second(args); - object *test = testargument(cddr(args)); - while (list != NULL) { - if (improperp(list)) error(notproper, list); - if (apply(test, cons(item, cons(car(list), NULL)), env) != NULL) return list; - list = cdr(list); - } - return nil; -} - -object *fn_apply (object *args, object *env) { - object *previous = NULL; - object *last = args; - while (cdr(last) != NULL) { - previous = last; - last = cdr(last); - } - object *arg = car(last); - if (!listp(arg)) error(notalist, arg); - cdr(previous) = arg; - return apply(first(args), cdr(args), env); -} - -object *fn_funcall (object *args, object *env) { - return apply(first(args), cdr(args), env); -} - -object *fn_append (object *args, object *env) { - (void) env; - object *head = NULL; - object *tail; - while (args != NULL) { - object *list = first(args); - if (!listp(list)) error(notalist, list); - while (consp(list)) { - object *obj = cons(car(list), cdr(list)); - if (head == NULL) head = obj; - else cdr(tail) = obj; - tail = obj; - list = cdr(list); - if (cdr(args) != NULL && improperp(list)) error(notproper, first(args)); - } - args = cdr(args); - } - return head; -} - -object *fn_mapc (object *args, object *env) { - return mapcl(args, env, false); -} - -object *fn_mapl (object *args, object *env) { - return mapcl(args, env, true); -} - -object *fn_mapcar (object *args, object *env) { - return mapcarcan(args, env, mapcarfun, false); -} - -object *fn_mapcan (object *args, object *env) { - return mapcarcan(args, env, mapcanfun, false); -} - -object *fn_maplist (object *args, object *env) { - return mapcarcan(args, env, mapcarfun, true); -} - -object *fn_mapcon (object *args, object *env) { - return mapcarcan(args, env, mapcanfun, true); -} - -// Arithmetic functions - -object *fn_add (object *args, object *env) { - (void) env; - int result = 0; - while (args != NULL) { - object *arg = car(args); - if (floatp(arg)) return add_floats(args, (float)result); - else if (integerp(arg)) { - int val = arg->integer; - if (val < 1) { if (INT_MIN - val > result) return add_floats(args, (float)result); } - else { if (INT_MAX - val < result) return add_floats(args, (float)result); } - result = result + val; - } else error(notanumber, arg); - args = cdr(args); - } - return number(result); -} - -object *fn_subtract (object *args, object *env) { - (void) env; - object *arg = car(args); - args = cdr(args); - if (args == NULL) return negate(arg); - else if (floatp(arg)) return subtract_floats(args, arg->single_float); - else if (integerp(arg)) { - int result = arg->integer; - while (args != NULL) { - arg = car(args); - if (floatp(arg)) return subtract_floats(args, result); - else if (integerp(arg)) { - int val = (car(args))->integer; - if (val < 1) { if (INT_MAX + val < result) return subtract_floats(args, result); } - else { if (INT_MIN + val > result) return subtract_floats(args, result); } - result = result - val; - } else error(notanumber, arg); - args = cdr(args); - } - return number(result); - } else error(notanumber, arg); - return nil; -} - -object *fn_multiply (object *args, object *env) { - (void) env; - int result = 1; - while (args != NULL){ - object *arg = car(args); - if (floatp(arg)) return multiply_floats(args, result); - else if (integerp(arg)) { - int64_t val = result * (int64_t)(arg->integer); - if ((val > INT_MAX) || (val < INT_MIN)) return multiply_floats(args, result); - result = val; - } else error(notanumber, arg); - args = cdr(args); - } - return number(result); -} - -object *fn_divide (object *args, object *env) { - (void) env; - object* arg = first(args); - args = cdr(args); - // One argument - if (args == NULL) { - if (floatp(arg)) { - float f = arg->single_float; - if (f == 0.0) error2("division by zero"); - return makefloat(1.0 / f); - } else if (integerp(arg)) { - int i = arg->integer; - if (i == 0) error2("division by zero"); - else if (i == 1) return number(1); - else return makefloat(1.0 / i); - } else error(notanumber, arg); - } - // Multiple arguments - if (floatp(arg)) return divide_floats(args, arg->single_float); - else if (integerp(arg)) { - int result = arg->integer; - while (args != NULL) { - arg = car(args); - if (floatp(arg)) { - return divide_floats(args, result); - } else if (integerp(arg)) { - int i = arg->integer; - if (i == 0) error2("division by zero"); - if ((result % i) != 0) return divide_floats(args, result); - if ((result == INT_MIN) && (i == -1)) return divide_floats(args, result); - result = result / i; - args = cdr(args); - } else error(notanumber, arg); - } - return number(result); - } else error(notanumber, arg); - return nil; -} - -object *fn_mod (object *args, object *env) { - (void) env; - object *arg1 = first(args); - object *arg2 = second(args); - if (integerp(arg1) && integerp(arg2)) { - int divisor = arg2->integer; - if (divisor == 0) error2("division by zero"); - int dividend = arg1->integer; - int remainder = dividend % divisor; - if ((dividend<0) != (divisor<0)) remainder = remainder + divisor; - return number(remainder); - } else { - float fdivisor = checkintfloat(arg2); - if (fdivisor == 0.0) error2("division by zero"); - float fdividend = checkintfloat(arg1); - float fremainder = fmod(fdividend , fdivisor); - if ((fdividend<0) != (fdivisor<0)) fremainder = fremainder + fdivisor; - return makefloat(fremainder); - } -} - -object *fn_oneplus (object *args, object *env) { - (void) env; - object* arg = first(args); - if (floatp(arg)) return makefloat((arg->single_float) + 1.0); - else if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MAX) return makefloat((arg->integer) + 1.0); - else return number(result + 1); - } else error(notanumber, arg); - return nil; -} - -object *fn_oneminus (object *args, object *env) { - (void) env; - object* arg = first(args); - if (floatp(arg)) return makefloat((arg->single_float) - 1.0); - else if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MIN) return makefloat((arg->integer) - 1.0); - else return number(result - 1); - } else error(notanumber, arg); - return nil; -} - -object *fn_abs (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return makefloat(abs(arg->single_float)); - else if (integerp(arg)) { - int result = arg->integer; - if (result == INT_MIN) return makefloat(abs((float)result)); - else return number(abs(result)); - } else error(notanumber, arg); - return nil; -} - -object *fn_random (object *args, object *env) { - (void) env; - object *arg = first(args); - if (integerp(arg)) return number(random(arg->integer)); - else if (floatp(arg)) return makefloat((float)rand()/(float)(RAND_MAX/(arg->single_float))); - else error(notanumber, arg); - return nil; -} - -object *fn_maxfn (object *args, object *env) { - (void) env; - object* result = first(args); - args = cdr(args); - while (args != NULL) { - object *arg = car(args); - if (integerp(result) && integerp(arg)) { - if ((arg->integer) > (result->integer)) result = arg; - } else if ((checkintfloat(arg) > checkintfloat(result))) result = arg; - args = cdr(args); - } - return result; -} - -object *fn_minfn (object *args, object *env) { - (void) env; - object* result = first(args); - args = cdr(args); - while (args != NULL) { - object *arg = car(args); - if (integerp(result) && integerp(arg)) { - if ((arg->integer) < (result->integer)) result = arg; - } else if ((checkintfloat(arg) < checkintfloat(result))) result = arg; - args = cdr(args); - } - return result; -} - -// Arithmetic comparisons - -object *fn_noteq (object *args, object *env) { - (void) env; - while (args != NULL) { - object *nargs = args; - object *arg1 = first(nargs); - nargs = cdr(nargs); - while (nargs != NULL) { - object *arg2 = first(nargs); - if (integerp(arg1) && integerp(arg2)) { - if ((arg1->integer) == (arg2->integer)) return nil; - } else if ((checkintfloat(arg1) == checkintfloat(arg2))) return nil; - nargs = cdr(nargs); - } - args = cdr(args); - } - return tee; -} - -object *fn_numeq (object *args, object *env) { - (void) env; - return compare(args, false, false, true); -} - -object *fn_less (object *args, object *env) { - (void) env; - return compare(args, true, false, false); -} - -object *fn_lesseq (object *args, object *env) { - (void) env; - return compare(args, true, false, true); -} - -object *fn_greater (object *args, object *env) { - (void) env; - return compare(args, false, true, false); -} - -object *fn_greatereq (object *args, object *env) { - (void) env; - return compare(args, false, true, true); -} - -object *fn_plusp (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return ((arg->single_float) > 0.0) ? tee : nil; - else if (integerp(arg)) return ((arg->integer) > 0) ? tee : nil; - else error(notanumber, arg); - return nil; -} - -object *fn_minusp (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return ((arg->single_float) < 0.0) ? tee : nil; - else if (integerp(arg)) return ((arg->integer) < 0) ? tee : nil; - else error(notanumber, arg); - return nil; -} - -object *fn_zerop (object *args, object *env) { - (void) env; - object *arg = first(args); - if (floatp(arg)) return ((arg->single_float) == 0.0) ? tee : nil; - else if (integerp(arg)) return ((arg->integer) == 0) ? tee : nil; - else error(notanumber, arg); - return nil; -} - -object *fn_oddp (object *args, object *env) { - (void) env; - int arg = checkinteger(first(args)); - return ((arg & 1) == 1) ? tee : nil; -} - -object *fn_evenp (object *args, object *env) { - (void) env; - int arg = checkinteger(first(args)); - return ((arg & 1) == 0) ? tee : nil; -} - -// Number functions - -object *fn_integerp (object *args, object *env) { - (void) env; - return integerp(first(args)) ? tee : nil; -} - -object *fn_numberp (object *args, object *env) { - (void) env; - object *arg = first(args); - return (integerp(arg) || floatp(arg)) ? tee : nil; -} - -// Floating-point functions - -object *fn_floatfn (object *args, object *env) { - (void) env; - object *arg = first(args); - return (floatp(arg)) ? arg : makefloat((float)(arg->integer)); -} - -object *fn_floatp (object *args, object *env) { - (void) env; - return floatp(first(args)) ? tee : nil; -} - -object *fn_sin (object *args, object *env) { - (void) env; - return makefloat(sin(checkintfloat(first(args)))); -} - -object *fn_cos (object *args, object *env) { - (void) env; - return makefloat(cos(checkintfloat(first(args)))); -} - -object *fn_tan (object *args, object *env) { - (void) env; - return makefloat(tan(checkintfloat(first(args)))); -} - -object *fn_asin (object *args, object *env) { - (void) env; - return makefloat(asin(checkintfloat(first(args)))); -} - -object *fn_acos (object *args, object *env) { - (void) env; - return makefloat(acos(checkintfloat(first(args)))); -} - -object *fn_atan (object *args, object *env) { - (void) env; - object *arg = first(args); - float div = 1.0; - args = cdr(args); - if (args != NULL) div = checkintfloat(first(args)); - return makefloat(atan2(checkintfloat(arg), div)); -} - -object *fn_sinh (object *args, object *env) { - (void) env; - return makefloat(sinh(checkintfloat(first(args)))); -} - -object *fn_cosh (object *args, object *env) { - (void) env; - return makefloat(cosh(checkintfloat(first(args)))); -} - -object *fn_tanh (object *args, object *env) { - (void) env; - return makefloat(tanh(checkintfloat(first(args)))); -} - -object *fn_exp (object *args, object *env) { - (void) env; - return makefloat(exp(checkintfloat(first(args)))); -} - -object *fn_sqrt (object *args, object *env) { - (void) env; - return makefloat(sqrt(checkintfloat(first(args)))); -} - -object *fn_log (object *args, object *env) { - (void) env; - object *arg = first(args); - float fresult = log(checkintfloat(arg)); - args = cdr(args); - if (args == NULL) return makefloat(fresult); - else return makefloat(fresult / log(checkintfloat(first(args)))); -} - -object *fn_expt (object *args, object *env) { - (void) env; - object *arg1 = first(args); object *arg2 = second(args); - float float1 = checkintfloat(arg1); - float value = log(abs(float1)) * checkintfloat(arg2); - if (integerp(arg1) && integerp(arg2) && ((arg2->integer) >= 0) && (abs(value) < 21.4875)) - return number(intpower(arg1->integer, arg2->integer)); - if (float1 < 0) { - if (integerp(arg2)) return makefloat((arg2->integer & 1) ? -exp(value) : exp(value)); - else error2("invalid result"); - } - return makefloat(exp(value)); -} - -object *fn_ceiling (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number(ceil(checkintfloat(arg) / checkintfloat(first(args)))); - else return number(ceil(checkintfloat(arg))); -} - -object *fn_floor (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number(floor(checkintfloat(arg) / checkintfloat(first(args)))); - else return number(floor(checkintfloat(arg))); -} - -object *fn_truncate (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number((int)(checkintfloat(arg) / checkintfloat(first(args)))); - else return number((int)(checkintfloat(arg))); -} - -object *fn_round (object *args, object *env) { - (void) env; - object *arg = first(args); - args = cdr(args); - if (args != NULL) return number(round(checkintfloat(arg) / checkintfloat(first(args)))); - else return number(round(checkintfloat(arg))); -} - -// Characters - -object *fn_char (object *args, object *env) { - (void) env; - object *arg = first(args); - if (!stringp(arg)) error(notastring, arg); - object *n = second(args); - char c = nthchar(arg, checkinteger(n)); - if (c == 0) error(indexrange, n); - return character(c); -} - -object *fn_charcode (object *args, object *env) { - (void) env; - return number(checkchar(first(args))); -} - -object *fn_codechar (object *args, object *env) { - (void) env; - return character(checkinteger(first(args))); -} - -object *fn_characterp (object *args, object *env) { - (void) env; - return characterp(first(args)) ? tee : nil; -} - -// Strings - -object *fn_stringp (object *args, object *env) { - (void) env; - return stringp(first(args)) ? tee : nil; -} - -object *fn_stringeq (object *args, object *env) { - (void) env; - int m = stringcompare(args, false, false, true); - return m == -1 ? nil : tee; -} - -object *fn_stringless (object *args, object *env) { - (void) env; - int m = stringcompare(args, true, false, false); - return m == -1 ? nil : number(m); -} - -object *fn_stringgreater (object *args, object *env) { - (void) env; - int m = stringcompare(args, false, true, false); - return m == -1 ? nil : number(m); -} - -object *fn_stringnoteq (object *args, object *env) { - (void) env; - int m = stringcompare(args, true, true, false); - return m == -1 ? nil : number(m); -} - -object *fn_stringlesseq (object *args, object *env) { - (void) env; - int m = stringcompare(args, true, false, true); - return m == -1 ? nil : number(m); -} - -object *fn_stringgreatereq (object *args, object *env) { - (void) env; - int m = stringcompare(args, false, true, true); - return m == -1 ? nil : number(m); -} - -object *fn_sort (object *args, object *env) { - if (first(args) == NULL) return nil; - object *list = cons(nil,first(args)); - protect(list); - object *predicate = second(args); - object *compare = cons(NULL, cons(NULL, NULL)); - protect(compare); - object *ptr = cdr(list); - while (cdr(ptr) != NULL) { - object *go = list; - while (go != ptr) { - car(compare) = car(cdr(ptr)); - car(cdr(compare)) = car(cdr(go)); - if (apply(predicate, compare, env)) break; - go = cdr(go); - } - if (go != ptr) { - object *obj = cdr(ptr); - cdr(ptr) = cdr(obj); - cdr(obj) = cdr(go); - cdr(go) = obj; - } else ptr = cdr(ptr); - } - unprotect(); unprotect(); - return cdr(list); -} - -object *fn_stringfn (object *args, object *env) { - return fn_princtostring(args, env); -} - -object *fn_concatenate (object *args, object *env) { - (void) env; - object *arg = first(args); - if (builtin(arg->name) != STRINGFN) error2("only supports strings"); - args = cdr(args); - object *result = newstring(); - object *tail = result; - while (args != NULL) { - object *obj = checkstring(first(args)); - obj = cdr(obj); - while (obj != NULL) { - int quad = obj->chars; - while (quad != 0) { - char ch = quad>>((sizeof(int)-1)*8) & 0xFF; - buildstring(ch, &tail); - quad = quad<<8; - } - obj = car(obj); - } - args = cdr(args); - } - return result; -} - -object *fn_subseq (object *args, object *env) { - (void) env; - object *arg = first(args); - int start = checkinteger(second(args)), end; - if (start < 0) error(indexnegative, second(args)); - args = cddr(args); - if (listp(arg)) { - int length = listlength(arg); - if (args != NULL) end = checkinteger(car(args)); else end = length; - if (start > end || end > length) error2(indexrange); - object *result = cons(NULL, NULL); - object *ptr = result; - for (int x = 0; x < end; x++) { - if (x >= start) { cdr(ptr) = cons(car(arg), NULL); ptr = cdr(ptr); } - arg = cdr(arg); - } - return cdr(result); - } else if (stringp(arg)) { - int length = stringlength(arg); - if (args != NULL) end = checkinteger(car(args)); else end = length; - if (start > end || end > length) error2(indexrange); - object *result = newstring(); - object *tail = result; - for (int i=start; i= 0) return number(value << count); - else return number(value >> abs(count)); -} - -object *fn_logbitp (object *args, object *env) { - (void) env; - int index = checkinteger(first(args)); - int value = checkinteger(second(args)); - return (bitRead(value, index) == 1) ? tee : nil; -} - -// System functions - -object *fn_eval (object *args, object *env) { - return eval(first(args), env); -} - -object *fn_return (object *args, object *env) { - (void) env; - setflag(RETURNFLAG); - if (args == NULL) return nil; else return first(args); -} - -object *fn_globals (object *args, object *env) { - (void) args, (void) env; - object *result = cons(NULL, NULL); - object *ptr = result; - object *arg = GlobalEnv; - while (arg != NULL) { - cdr(ptr) = cons(car(car(arg)), NULL); ptr = cdr(ptr); - arg = cdr(arg); - } - return cdr(result); -} - -object *fn_locals (object *args, object *env) { - (void) args; - return env; -} - -object *fn_makunbound (object *args, object *env) { - (void) env; - object *var = first(args); - if (!symbolp(var)) error(notasymbol, var); - delassoc(var, &GlobalEnv); - return var; -} - -object *fn_break (object *args, object *env) { - (void) args; - pfstring("\nBreak!\n", pserial); - BreakLevel++; - repl(env); - BreakLevel--; - return nil; -} - -object *fn_read (object *args, object *env) { - (void) env; - gfun_t gfun = gstreamfun(args); - return read(gfun); -} - -object *fn_prin1 (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - printobject(obj, pfun); - return obj; -} - -object *fn_print (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - pln(pfun); - printobject(obj, pfun); - pfun(' '); - return obj; -} - -object *fn_princ (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - prin1object(obj, pfun); - return obj; -} - -object *fn_terpri (object *args, object *env) { - (void) env; - pfun_t pfun = pstreamfun(args); - pln(pfun); - return nil; -} - -object *fn_readbyte (object *args, object *env) { - (void) env; - gfun_t gfun = gstreamfun(args); - int c = gfun(); - return (c == -1) ? nil : number(c); -} - -object *fn_readline (object *args, object *env) { - (void) env; - gfun_t gfun = gstreamfun(args); - return readstring('\n', false, gfun); -} - -object *fn_writebyte (object *args, object *env) { - (void) env; - int value = checkinteger(first(args)); - pfun_t pfun = pstreamfun(cdr(args)); - (pfun)(value); - return nil; -} - -object *fn_writestring (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - char temp = Flags; - clrflag(PRINTREADABLY); - printstring(obj, pfun); - Flags = temp; - return nil; -} - -object *fn_writeline (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - char temp = Flags; - clrflag(PRINTREADABLY); - printstring(obj, pfun); - pln(pfun); - Flags = temp; - return nil; -} - -object *fn_restarti2c (object *args, object *env) { - (void) env; - int stream = isstream(first(args)); - args = cdr(args); - int read = 0; // Write - I2Ccount = 0; - if (args != NULL) { - object *rw = first(args); - if (integerp(rw)) I2Ccount = rw->integer; - read = (rw != NULL); - } - int address = stream & 0xFF; - if (stream>>8 != I2CSTREAM) error2("not an i2c stream"); - TwoWire *port; - if (address < 128) port = &Wire; - #if defined(ULISP_I2C1) - else port = &Wire1; - #endif - return I2Crestart(port, address & 0x7F, read) ? tee : nil; -} - -object *fn_gc (object *obj, object *env) { - int initial = Freespace; - unsigned long start = micros(); - gc(obj, env); - unsigned long elapsed = micros() - start; - pfstring("Space: ", pserial); - pint(Freespace - initial, pserial); - pfstring(" bytes, Time: ", pserial); - pint(elapsed, pserial); - pfstring(" us\n", pserial); - return nil; -} - -object *fn_room (object *args, object *env) { - (void) args, (void) env; - return number(Freespace); -} - -object *fn_saveimage (object *args, object *env) { - if (args != NULL) args = eval(first(args), env); - return number(saveimage(args)); -} - -object *fn_loadimage (object *args, object *env) { - (void) env; - if (args != NULL) args = first(args); - return number(loadimage(args)); -} - -object *fn_cls (object *args, object *env) { - (void) args, (void) env; - pserial(12); - return nil; -} - -// Arduino procedures - -object *fn_pinmode (object *args, object *env) { - (void) env; int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(first(args)); - int pm = INPUT; - arg = second(args); - if (keywordp(arg)) pm = checkkeyword(arg); - else if (integerp(arg)) { - int mode = arg->integer; - if (mode == 1) pm = OUTPUT; else if (mode == 2) pm = INPUT_PULLUP; - #if defined(INPUT_PULLDOWN) - else if (mode == 4) pm = INPUT_PULLDOWN; - #endif - } else if (arg != nil) pm = OUTPUT; - pinMode(pin, pm); - return nil; -} - -object *fn_digitalread (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(arg); - if (digitalRead(pin) != 0) return tee; else return nil; -} - -object *fn_digitalwrite (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(arg); - arg = second(args); - int mode; - if (keywordp(arg)) mode = checkkeyword(arg); - else if (integerp(arg)) mode = arg->integer ? HIGH : LOW; - else mode = (arg != nil) ? HIGH : LOW; - digitalWrite(pin, mode); - return arg; -} - -object *fn_analogread (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else { - pin = checkinteger(arg); - checkanalogread(pin); - } - return number(analogRead(pin)); -} - -object *fn_analogreadresolution (object *args, object *env) { - (void) env; - object *arg = first(args); - #if defined(ESP32) - analogReadResolution(checkinteger(arg)); - #else - error2("not supported"); - #endif - return arg; -} - -object *fn_analogwrite (object *args, object *env) { - (void) env; - int pin; - object *arg = first(args); - if (keywordp(arg)) pin = checkkeyword(arg); - else pin = checkinteger(arg); - checkanalogwrite(pin); - object *value = second(args); - analogWrite(pin, checkinteger(value)); - return value; -} - -object *fn_delay (object *args, object *env) { - (void) env; - object *arg1 = first(args); - unsigned long start = millis(); - unsigned long total = checkinteger(arg1); - do testescape(); - while (millis() - start < total); - return arg1; -} - -object *fn_millis (object *args, object *env) { - (void) args, (void) env; - return number(millis()); -} - -object *fn_sleep (object *args, object *env) { - (void) env; - object *arg1 = first(args); - doze(checkinteger(arg1)); - return arg1; -} - -object *fn_note (object *args, object *env) { - (void) env; - static int pin = 255; - if (args != NULL) { - pin = checkinteger(first(args)); - int note = 48, octave = 0; - if (cdr(args) != NULL) { - note = checkinteger(second(args)); - if (cddr(args) != NULL) octave = checkinteger(third(args)); - } - playnote(pin, note, octave); - } else nonote(pin); - return nil; -} - -object *fn_register (object *args, object *env) { - (void) env; - object *arg = first(args); - int addr; - if (keywordp(arg)) addr = checkkeyword(arg); - else addr = checkinteger(first(args)); - if (cdr(args) == NULL) return number(*(uint32_t *)addr); - (*(uint32_t *)addr) = checkinteger(second(args)); - return second(args); -} - -// Tree Editor - -object *fn_edit (object *args, object *env) { - object *fun = first(args); - object *pair = findvalue(fun, env); - clrflag(EXITEDITOR); - object *arg = edit(eval(fun, env)); - cdr(pair) = arg; - return arg; -} - -// Pretty printer - -object *fn_pprint (object *args, object *env) { - (void) env; - object *obj = first(args); - pfun_t pfun = pstreamfun(cdr(args)); - #if defined(gfxsupport) - if (pfun == gfxwrite) ppwidth = GFXPPWIDTH; - #endif - pln(pfun); - superprint(obj, 0, pfun); - ppwidth = PPWIDTH; - return bsymbol(NOTHING); -} - -object *fn_pprintall (object *args, object *env) { - (void) env; - pfun_t pfun = pstreamfun(args); - #if defined(gfxsupport) - if (pfun == gfxwrite) ppwidth = GFXPPWIDTH; - #endif - object *globals = GlobalEnv; - while (globals != NULL) { - object *pair = first(globals); - object *var = car(pair); - object *val = cdr(pair); - pln(pfun); - if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) { - superprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun); - } else { - superprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun); - } - pln(pfun); - testescape(); - globals = cdr(globals); - } - ppwidth = PPWIDTH; - return bsymbol(NOTHING); -} - -// Format - -object *fn_format (object *args, object *env) { - (void) env; - pfun_t pfun = pserial; - object *output = first(args); - object *obj; - if (output == nil) { obj = startstring(); pfun = pstr; } - else if (output != tee) pfun = pstreamfun(args); - object *formatstr = checkstring(second(args)); - object *save = NULL; - args = cddr(args); - int len = stringlength(formatstr); - uint8_t n = 0, width = 0, w, bra = 0; - char pad = ' '; - bool tilde = false, mute = false, comma = false, quote = false; - while (n < len) { - char ch = nthchar(formatstr, n); - char ch2 = ch & ~0x20; // force to upper case - if (tilde) { - if (ch == '}') { - if (save == NULL) formaterr(formatstr, "no matching ~{", n); - if (args == NULL) { args = cdr(save); save = NULL; } else n = bra; - mute = false; tilde = false; - } - else if (!mute) { - if (comma && quote) { pad = ch; comma = false, quote = false; } - else if (ch == '\'') { - if (comma) quote = true; - else formaterr(formatstr, "quote not valid", n); - } - else if (ch == '~') { pfun('~'); tilde = false; } - else if (ch >= '0' && ch <= '9') width = width*10 + ch - '0'; - else if (ch == ',') comma = true; - else if (ch == '%') { pln(pfun); tilde = false; } - else if (ch == '&') { pfl(pfun); tilde = false; } - else if (ch == '^') { - if (save != NULL && args == NULL) mute = true; - tilde = false; - } - else if (ch == '{') { - if (save != NULL) formaterr(formatstr, "can't nest ~{", n); - if (args == NULL) formaterr(formatstr, noargument, n); - if (!listp(first(args))) formaterr(formatstr, notalist, n); - save = args; args = first(args); bra = n; tilde = false; - if (args == NULL) mute = true; - } - else if (ch2 == 'A' || ch2 == 'S' || ch2 == 'D' || ch2 == 'G' || ch2 == 'X' || ch2 == 'B') { - if (args == NULL) formaterr(formatstr, noargument, n); - object *arg = first(args); args = cdr(args); - uint8_t aw = atomwidth(arg); - if (width < aw) w = 0; else w = width-aw; - tilde = false; - if (ch2 == 'A') { prin1object(arg, pfun); indent(w, pad, pfun); } - else if (ch2 == 'S') { printobject(arg, pfun); indent(w, pad, pfun); } - else if (ch2 == 'D' || ch2 == 'G') { indent(w, pad, pfun); prin1object(arg, pfun); } - else if (ch2 == 'X' || ch2 == 'B') { - if (integerp(arg)) { - uint8_t base = (ch2 == 'B') ? 2 : 16; - uint8_t hw = basewidth(arg, base); if (width < hw) w = 0; else w = width-hw; - indent(w, pad, pfun); pintbase(arg->integer, base, pfun); - } else { - indent(w, pad, pfun); prin1object(arg, pfun); - } - } - tilde = false; - } else formaterr(formatstr, "invalid directive", n); - } - } else { - if (ch == '~') { tilde = true; pad = ' '; width = 0; comma = false; quote = false; } - else if (!mute) pfun(ch); - } - n++; - } - if (output == nil) return obj; - else return nil; -} - -// LispLibrary - -object *fn_require (object *args, object *env) { - object *arg = first(args); - object *globals = GlobalEnv; - if (!symbolp(arg)) error(notasymbol, arg); - while (globals != NULL) { - object *pair = first(globals); - object *var = car(pair); - if (symbolp(var) && var == arg) return nil; - globals = cdr(globals); - } - GlobalStringIndex = 0; - object *line = read(glibrary); - while (line != NULL) { - // Is this the definition we want - symbol_t fname = first(line)->name; - if ((fname == sym(DEFUN) || fname == sym(DEFVAR)) && symbolp(second(line)) && second(line)->name == arg->name) { - eval(line, env); - return tee; - } - line = read(glibrary); - } - return nil; -} - -object *fn_listlibrary (object *args, object *env) { - (void) args, (void) env; - GlobalStringIndex = 0; - object *line = read(glibrary); - while (line != NULL) { - builtin_t bname = builtin(first(line)->name); - if (bname == DEFUN || bname == DEFVAR) { - printsymbol(second(line), pserial); pserial(' '); - } - line = read(glibrary); - } - return bsymbol(NOTHING); -} - -// Documentation - -object *sp_help (object *args, object *env) { - if (args == NULL) error2(noargument); - object *docstring = documentation(first(args), env); - if (docstring) { - char temp = Flags; - clrflag(PRINTREADABLY); - printstring(docstring, pserial); - Flags = temp; - } - return bsymbol(NOTHING); -} - -object *fn_documentation (object *args, object *env) { - return documentation(first(args), env); -} - -object *fn_apropos (object *args, object *env) { - (void) env; - apropos(first(args), true); - return bsymbol(NOTHING); -} - -object *fn_aproposlist (object *args, object *env) { - (void) env; - return apropos(first(args), false); -} - -// Error handling - -object *sp_unwindprotect (object *args, object *env) { - if (args == NULL) error2(toofewargs); - object *current_GCStack = GCStack; - jmp_buf dynamic_handler; - jmp_buf *previous_handler = handler; - handler = &dynamic_handler; - object *protected_form = first(args); - object *result; - - bool signaled = false; - if (!setjmp(dynamic_handler)) { - result = eval(protected_form, env); - } else { - GCStack = current_GCStack; - signaled = true; - } - handler = previous_handler; - - object *protective_forms = cdr(args); - while (protective_forms != NULL) { - eval(car(protective_forms), env); - if (tstflag(RETURNFLAG)) break; - protective_forms = cdr(protective_forms); - } - - if (!signaled) return result; - GCStack = NULL; - longjmp(*handler, 1); -} - -object *sp_ignoreerrors (object *args, object *env) { - object *current_GCStack = GCStack; - jmp_buf dynamic_handler; - jmp_buf *previous_handler = handler; - handler = &dynamic_handler; - object *result = nil; - - bool muffled = tstflag(MUFFLEERRORS); - setflag(MUFFLEERRORS); - bool signaled = false; - if (!setjmp(dynamic_handler)) { - while (args != NULL) { - result = eval(car(args), env); - if (tstflag(RETURNFLAG)) break; - args = cdr(args); - } - } else { - GCStack = current_GCStack; - signaled = true; - } - handler = previous_handler; - if (!muffled) clrflag(MUFFLEERRORS); - - if (signaled) return bsymbol(NOTHING); - else return result; -} - -object *sp_error (object *args, object *env) { - object *message = eval(cons(bsymbol(FORMAT), cons(nil, args)), env); - if (!tstflag(MUFFLEERRORS)) { - char temp = Flags; - clrflag(PRINTREADABLY); - pfstring("Error: ", pserial); printstring(message, pserial); - Flags = temp; - pln(pserial); - } - GCStack = NULL; - longjmp(*handler, 1); -} - -// Wi-Fi - -object *sp_withclient (object *args, object *env) { - object *params = checkarguments(args, 1, 3); - object *var = first(params); - char buffer[BUFFERSIZE]; - params = cdr(params); - int n; - if (params == NULL) { - client = server.available(); - if (!client) return nil; - n = 2; - } else { - object *address = eval(first(params), env); - object *port = eval(second(params), env); - int success; - if (stringp(address)) success = client.connect(cstring(address, buffer, BUFFERSIZE), checkinteger(port)); - else if (integerp(address)) success = client.connect(address->integer, checkinteger(port)); - else error2("invalid address"); - if (!success) return nil; - n = 1; - } - object *pair = cons(var, stream(WIFISTREAM, n)); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - client.stop(); - return result; -} - -object *fn_available (object *args, object *env) { - (void) env; - if (isstream(first(args))>>8 != WIFISTREAM) error2("invalid stream"); - return number(client.available()); -} - -object *fn_wifiserver (object *args, object *env) { - (void) args, (void) env; - server.begin(); - return nil; -} - -object *fn_wifisoftap (object *args, object *env) { - (void) env; - char ssid[33], pass[65]; - if (args == NULL) return WiFi.softAPdisconnect(true) ? tee : nil; - object *first = first(args); args = cdr(args); - if (args == NULL) WiFi.softAP(cstring(first, ssid, 33)); - else { - object *second = first(args); - args = cdr(args); - int channel = 1; - bool hidden = false; - if (args != NULL) { - channel = checkinteger(first(args)); - args = cdr(args); - if (args != NULL) hidden = (first(args) != nil); - } - WiFi.softAP(cstring(first, ssid, 33), cstring(second, pass, 65), channel, hidden); - } - return iptostring(WiFi.softAPIP()); -} - -object *fn_connected (object *args, object *env) { - (void) env; - if (isstream(first(args))>>8 != WIFISTREAM) error2("invalid stream"); - return client.connected() ? tee : nil; -} - -object *fn_wifilocalip (object *args, object *env) { - (void) args, (void) env; - return iptostring(WiFi.localIP()); -} - -object *fn_wificonnect (object *args, object *env) { - (void) env; - char ssid[33], pass[65]; - if (args == NULL) { WiFi.disconnect(true); return nil; } - if (cdr(args) == NULL) WiFi.begin(cstring(first(args), ssid, 33)); - else WiFi.begin(cstring(first(args), ssid, 33), cstring(second(args), pass, 65)); - int result = WiFi.waitForConnectResult(); - if (result == WL_CONNECTED) return iptostring(WiFi.localIP()); - else if (result == WL_NO_SSID_AVAIL) error2("network not found"); - else if (result == WL_CONNECT_FAILED) error2("connection failed"); - else error2("unable to connect"); - return nil; -} - -// Graphics functions - -object *sp_withgfx (object *args, object *env) { -#if defined(gfxsupport) - object *params = checkarguments(args, 1, 1); - object *var = first(params); - object *pair = cons(var, stream(GFXSTREAM, 1)); - push(pair,env); - object *forms = cdr(args); - object *result = eval(tf_progn(forms,env), env); - return result; -#else - (void) args, (void) env; - error2("not supported"); - return nil; -#endif -} - -object *fn_drawpixel (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t colour = COLOR_WHITE; - if (cddr(args) != NULL) colour = checkinteger(third(args)); - tft.drawPixel(checkinteger(first(args)), checkinteger(second(args)), colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_drawline (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[4], colour = COLOR_WHITE; - for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawLine(params[0], params[1], params[2], params[3], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_drawrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[4], colour = COLOR_WHITE; - for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawRect(params[0], params[1], params[2], params[3], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_fillrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[4], colour = COLOR_WHITE; - for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillRect(params[0], params[1], params[2], params[3], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_drawcircle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[3], colour = COLOR_WHITE; - for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawCircle(params[0], params[1], params[2], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_fillcircle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[3], colour = COLOR_WHITE; - for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillCircle(params[0], params[1], params[2], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_drawroundrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[5], colour = COLOR_WHITE; - for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawRoundRect(params[0], params[1], params[2], params[3], params[4], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_fillroundrect (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[5], colour = COLOR_WHITE; - for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillRoundRect(params[0], params[1], params[2], params[3], params[4], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_drawtriangle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[6], colour = COLOR_WHITE; - for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.drawTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_filltriangle (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t params[6], colour = COLOR_WHITE; - for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } - if (args != NULL) colour = checkinteger(car(args)); - tft.fillTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_drawchar (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t colour = COLOR_WHITE, bg = COLOR_BLACK, size = 1; - object *more = cdr(cddr(args)); - if (more != NULL) { - colour = checkinteger(car(more)); - more = cdr(more); - if (more != NULL) { - bg = checkinteger(car(more)); - more = cdr(more); - if (more != NULL) size = checkinteger(car(more)); - } - } - tft.drawChar(checkinteger(first(args)), checkinteger(second(args)), checkchar(third(args)), - colour, bg, size); - #else - (void) args; - #endif - return nil; -} - -object *fn_setcursor (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setCursor(checkinteger(first(args)), checkinteger(second(args))); - #else - (void) args; - #endif - return nil; -} - -object *fn_settextcolor (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - if (cdr(args) != NULL) tft.setTextColor(checkinteger(first(args)), checkinteger(second(args))); - else tft.setTextColor(checkinteger(first(args))); - #else - (void) args; - #endif - return nil; -} - -object *fn_settextsize (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setTextSize(checkinteger(first(args))); - #else - (void) args; - #endif - return nil; -} - -object *fn_settextwrap (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setTextWrap(first(args) != NULL); - #else - (void) args; - #endif - return nil; -} - -object *fn_fillscreen (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - uint16_t colour = COLOR_BLACK; - if (args != NULL) colour = checkinteger(first(args)); - tft.fillScreen(colour); - #else - (void) args; - #endif - return nil; -} - -object *fn_setrotation (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.setRotation(checkinteger(first(args))); - #else - (void) args; - #endif - return nil; -} - -object *fn_invertdisplay (object *args, object *env) { - (void) env; - #if defined(gfxsupport) - tft.invertDisplay(first(args) != NULL); - #else - (void) args; - #endif - return nil; -} - -// Built-in symbol names -const char string0[] PROGMEM = "nil"; -const char string1[] PROGMEM = "t"; -const char string2[] PROGMEM = "nothing"; -const char string3[] PROGMEM = "&optional"; -const char string4[] PROGMEM = "*features*"; -const char string5[] PROGMEM = ":initial-element"; -const char string6[] PROGMEM = ":element-type"; -const char string7[] PROGMEM = ":test"; -const char string8[] PROGMEM = "bit"; -const char string9[] PROGMEM = "&rest"; -const char string10[] PROGMEM = "lambda"; -const char string11[] PROGMEM = "let"; -const char string12[] PROGMEM = "let*"; -const char string13[] PROGMEM = "closure"; -const char string14[] PROGMEM = "*pc*"; -const char string15[] PROGMEM = "quote"; -const char string16[] PROGMEM = "defun"; -const char string17[] PROGMEM = "defvar"; -const char string18[] PROGMEM = "eq"; -const char string19[] PROGMEM = "car"; -const char string20[] PROGMEM = "first"; -const char string21[] PROGMEM = "cdr"; -const char string22[] PROGMEM = "rest"; -const char string23[] PROGMEM = "nth"; -const char string24[] PROGMEM = "aref"; -const char string25[] PROGMEM = "char"; -const char string26[] PROGMEM = "string"; -const char string27[] PROGMEM = "pinmode"; -const char string28[] PROGMEM = "digitalwrite"; -const char string29[] PROGMEM = "analogread"; -const char string30[] PROGMEM = "register"; -const char string31[] PROGMEM = "format"; -const char string32[] PROGMEM = "or"; -const char string33[] PROGMEM = "setq"; -const char string34[] PROGMEM = "loop"; -const char string35[] PROGMEM = "push"; -const char string36[] PROGMEM = "pop"; -const char string37[] PROGMEM = "incf"; -const char string38[] PROGMEM = "decf"; -const char string39[] PROGMEM = "setf"; -const char string40[] PROGMEM = "dolist"; -const char string41[] PROGMEM = "dotimes"; -const char string42[] PROGMEM = "do"; -const char string43[] PROGMEM = "do*"; -const char string44[] PROGMEM = "trace"; -const char string45[] PROGMEM = "untrace"; -const char string46[] PROGMEM = "for-millis"; -const char string47[] PROGMEM = "time"; -const char string48[] PROGMEM = "with-output-to-string"; -const char string49[] PROGMEM = "with-serial"; -const char string50[] PROGMEM = "with-i2c"; -const char string51[] PROGMEM = "with-sd-card"; -const char string52[] PROGMEM = "progn"; -const char string53[] PROGMEM = "if"; -const char string54[] PROGMEM = "cond"; -const char string55[] PROGMEM = "when"; -const char string56[] PROGMEM = "unless"; -const char string57[] PROGMEM = "case"; -const char string58[] PROGMEM = "and"; -const char string59[] PROGMEM = "not"; -const char string60[] PROGMEM = "null"; -const char string61[] PROGMEM = "cons"; -const char string62[] PROGMEM = "atom"; -const char string63[] PROGMEM = "listp"; -const char string64[] PROGMEM = "consp"; -const char string65[] PROGMEM = "symbolp"; -const char string66[] PROGMEM = "arrayp"; -const char string67[] PROGMEM = "boundp"; -const char string68[] PROGMEM = "keywordp"; -const char string69[] PROGMEM = "set"; -const char string70[] PROGMEM = "streamp"; -const char string71[] PROGMEM = "equal"; -const char string72[] PROGMEM = "caar"; -const char string73[] PROGMEM = "cadr"; -const char string74[] PROGMEM = "second"; -const char string75[] PROGMEM = "cdar"; -const char string76[] PROGMEM = "cddr"; -const char string77[] PROGMEM = "caaar"; -const char string78[] PROGMEM = "caadr"; -const char string79[] PROGMEM = "cadar"; -const char string80[] PROGMEM = "caddr"; -const char string81[] PROGMEM = "third"; -const char string82[] PROGMEM = "cdaar"; -const char string83[] PROGMEM = "cdadr"; -const char string84[] PROGMEM = "cddar"; -const char string85[] PROGMEM = "cdddr"; -const char string86[] PROGMEM = "length"; -const char string87[] PROGMEM = "array-dimensions"; -const char string88[] PROGMEM = "list"; -const char string89[] PROGMEM = "copy-list"; -const char string90[] PROGMEM = "make-array"; -const char string91[] PROGMEM = "reverse"; -const char string92[] PROGMEM = "assoc"; -const char string93[] PROGMEM = "member"; -const char string94[] PROGMEM = "apply"; -const char string95[] PROGMEM = "funcall"; -const char string96[] PROGMEM = "append"; -const char string97[] PROGMEM = "mapc"; -const char string98[] PROGMEM = "mapl"; -const char string99[] PROGMEM = "mapcar"; -const char string100[] PROGMEM = "mapcan"; -const char string101[] PROGMEM = "maplist"; -const char string102[] PROGMEM = "mapcon"; -const char string103[] PROGMEM = "+"; -const char string104[] PROGMEM = "-"; -const char string105[] PROGMEM = "*"; -const char string106[] PROGMEM = "/"; -const char string107[] PROGMEM = "mod"; -const char string108[] PROGMEM = "1+"; -const char string109[] PROGMEM = "1-"; -const char string110[] PROGMEM = "abs"; -const char string111[] PROGMEM = "random"; -const char string112[] PROGMEM = "max"; -const char string113[] PROGMEM = "min"; -const char string114[] PROGMEM = "/="; -const char string115[] PROGMEM = "="; -const char string116[] PROGMEM = "<"; -const char string117[] PROGMEM = "<="; -const char string118[] PROGMEM = ">"; -const char string119[] PROGMEM = ">="; -const char string120[] PROGMEM = "plusp"; -const char string121[] PROGMEM = "minusp"; -const char string122[] PROGMEM = "zerop"; -const char string123[] PROGMEM = "oddp"; -const char string124[] PROGMEM = "evenp"; -const char string125[] PROGMEM = "integerp"; -const char string126[] PROGMEM = "numberp"; -const char string127[] PROGMEM = "float"; -const char string128[] PROGMEM = "floatp"; -const char string129[] PROGMEM = "sin"; -const char string130[] PROGMEM = "cos"; -const char string131[] PROGMEM = "tan"; -const char string132[] PROGMEM = "asin"; -const char string133[] PROGMEM = "acos"; -const char string134[] PROGMEM = "atan"; -const char string135[] PROGMEM = "sinh"; -const char string136[] PROGMEM = "cosh"; -const char string137[] PROGMEM = "tanh"; -const char string138[] PROGMEM = "exp"; -const char string139[] PROGMEM = "sqrt"; -const char string140[] PROGMEM = "log"; -const char string141[] PROGMEM = "expt"; -const char string142[] PROGMEM = "ceiling"; -const char string143[] PROGMEM = "floor"; -const char string144[] PROGMEM = "truncate"; -const char string145[] PROGMEM = "round"; -const char string146[] PROGMEM = "char-code"; -const char string147[] PROGMEM = "code-char"; -const char string148[] PROGMEM = "characterp"; -const char string149[] PROGMEM = "stringp"; -const char string150[] PROGMEM = "string="; -const char string151[] PROGMEM = "string<"; -const char string152[] PROGMEM = "string>"; -const char string153[] PROGMEM = "string/="; -const char string154[] PROGMEM = "string<="; -const char string155[] PROGMEM = "string>="; -const char string156[] PROGMEM = "sort"; -const char string157[] PROGMEM = "concatenate"; -const char string158[] PROGMEM = "subseq"; -const char string159[] PROGMEM = "search"; -const char string160[] PROGMEM = "read-from-string"; -const char string161[] PROGMEM = "princ-to-string"; -const char string162[] PROGMEM = "prin1-to-string"; -const char string163[] PROGMEM = "logand"; -const char string164[] PROGMEM = "logior"; -const char string165[] PROGMEM = "logxor"; -const char string166[] PROGMEM = "lognot"; -const char string167[] PROGMEM = "ash"; -const char string168[] PROGMEM = "logbitp"; -const char string169[] PROGMEM = "eval"; -const char string170[] PROGMEM = "return"; -const char string171[] PROGMEM = "globals"; -const char string172[] PROGMEM = "locals"; -const char string173[] PROGMEM = "makunbound"; -const char string174[] PROGMEM = "break"; -const char string175[] PROGMEM = "read"; -const char string176[] PROGMEM = "prin1"; -const char string177[] PROGMEM = "print"; -const char string178[] PROGMEM = "princ"; -const char string179[] PROGMEM = "terpri"; -const char string180[] PROGMEM = "read-byte"; -const char string181[] PROGMEM = "read-line"; -const char string182[] PROGMEM = "write-byte"; -const char string183[] PROGMEM = "write-string"; -const char string184[] PROGMEM = "write-line"; -const char string185[] PROGMEM = "restart-i2c"; -const char string186[] PROGMEM = "gc"; -const char string187[] PROGMEM = "room"; -const char string188[] PROGMEM = "save-image"; -const char string189[] PROGMEM = "load-image"; -const char string190[] PROGMEM = "cls"; -const char string191[] PROGMEM = "digitalread"; -const char string192[] PROGMEM = "analogreadresolution"; -const char string193[] PROGMEM = "analogwrite"; -const char string194[] PROGMEM = "delay"; -const char string195[] PROGMEM = "millis"; -const char string196[] PROGMEM = "sleep"; -const char string197[] PROGMEM = "note"; -const char string198[] PROGMEM = "edit"; -const char string199[] PROGMEM = "pprint"; -const char string200[] PROGMEM = "pprintall"; -const char string201[] PROGMEM = "require"; -const char string202[] PROGMEM = "list-library"; -const char string203[] PROGMEM = "?"; -const char string204[] PROGMEM = "documentation"; -const char string205[] PROGMEM = "apropos"; -const char string206[] PROGMEM = "apropos-list"; -const char string207[] PROGMEM = "unwind-protect"; -const char string208[] PROGMEM = "ignore-errors"; -const char string209[] PROGMEM = "error"; -const char string210[] PROGMEM = "with-client"; -const char string211[] PROGMEM = "available"; -const char string212[] PROGMEM = "wifi-server"; -const char string213[] PROGMEM = "wifi-softap"; -const char string214[] PROGMEM = "connected"; -const char string215[] PROGMEM = "wifi-localip"; -const char string216[] PROGMEM = "wifi-connect"; -const char string217[] PROGMEM = "with-gfx"; -const char string218[] PROGMEM = "draw-pixel"; -const char string219[] PROGMEM = "draw-line"; -const char string220[] PROGMEM = "draw-rect"; -const char string221[] PROGMEM = "fill-rect"; -const char string222[] PROGMEM = "draw-circle"; -const char string223[] PROGMEM = "fill-circle"; -const char string224[] PROGMEM = "draw-round-rect"; -const char string225[] PROGMEM = "fill-round-rect"; -const char string226[] PROGMEM = "draw-triangle"; -const char string227[] PROGMEM = "fill-triangle"; -const char string228[] PROGMEM = "draw-char"; -const char string229[] PROGMEM = "set-cursor"; -const char string230[] PROGMEM = "set-text-color"; -const char string231[] PROGMEM = "set-text-size"; -const char string232[] PROGMEM = "set-text-wrap"; -const char string233[] PROGMEM = "fill-screen"; -const char string234[] PROGMEM = "set-rotation"; -const char string235[] PROGMEM = "invert-display"; -const char string236[] PROGMEM = ":led-builtin"; -const char string237[] PROGMEM = ":high"; -const char string238[] PROGMEM = ":low"; -const char string239[] PROGMEM = ":input"; -const char string240[] PROGMEM = ":input-pullup"; -const char string241[] PROGMEM = ":input-pulldown"; -const char string242[] PROGMEM = ":output"; - -// Documentation strings -const char doc0[] PROGMEM = "nil\n" -"A symbol equivalent to the empty list (). Also represents false."; -const char doc1[] PROGMEM = "t\n" -"A symbol representing true."; -const char doc2[] PROGMEM = "nothing\n" -"A symbol with no value.\n" -"It is useful if you want to suppress printing the result of evaluating a function."; -const char doc3[] PROGMEM = "&optional\n" -"Can be followed by one or more optional parameters in a lambda or defun parameter list."; -const char doc4[] PROGMEM = "*features*\n" -"Returns a list of keywords representing features supported by this platform."; -const char doc9[] PROGMEM = "&rest\n" -"Can be followed by a parameter in a lambda or defun parameter list,\n" -"and is assigned a list of the corresponding arguments."; -const char doc10[] PROGMEM = "(lambda (parameter*) form*)\n" -"Creates an unnamed function with parameters. The body is evaluated with the parameters as local variables\n" -"whose initial values are defined by the values of the forms after the lambda form."; -const char doc11[] PROGMEM = "(let ((var value) ... ) forms*)\n" -"Declares local variables with values, and evaluates the forms with those local variables."; -const char doc12[] PROGMEM = "(let* ((var value) ... ) forms*)\n" -"Declares local variables with values, and evaluates the forms with those local variables.\n" -"Each declaration can refer to local variables that have been defined earlier in the let*."; -const char doc16[] PROGMEM = "(defun name (parameters) form*)\n" -"Defines a function."; -const char doc17[] PROGMEM = "(defvar variable form)\n" -"Defines a global variable."; -const char doc18[] PROGMEM = "(eq item item)\n" -"Tests whether the two arguments are the same symbol, same character, equal numbers,\n" -"or point to the same cons, and returns t or nil as appropriate."; -const char doc19[] PROGMEM = "(car list)\n" -"Returns the first item in a list."; -const char doc21[] PROGMEM = "(cdr list)\n" -"Returns a list with the first item removed."; -const char doc23[] PROGMEM = "(nth number list)\n" -"Returns the nth item in list, counting from zero."; -const char doc24[] PROGMEM = "(aref array index [index*])\n" -"Returns an element from the specified array."; -const char doc25[] PROGMEM = "(char string n)\n" -"Returns the nth character in a string, counting from zero."; -const char doc26[] PROGMEM = "(string item)\n" -"Converts its argument to a string."; -const char doc27[] PROGMEM = "(pinmode pin mode)\n" -"Sets the input/output mode of an Arduino pin number, and returns nil.\n" -"The mode parameter can be an integer, a keyword, or t or nil."; -const char doc28[] PROGMEM = "(digitalwrite pin state)\n" -"Sets the state of the specified Arduino pin number."; -const char doc29[] PROGMEM = "(analogread pin)\n" -"Reads the specified Arduino analogue pin number and returns the value."; -const char doc30[] PROGMEM = "(register address [value])\n" -"Reads or writes the value of a peripheral register.\n" -"If value is not specified the function returns the value of the register at address.\n" -"If value is specified the value is written to the register at address and the function returns value."; -const char doc31[] PROGMEM = "(format output controlstring [arguments]*)\n" -"Outputs its arguments formatted according to the format directives in controlstring."; -const char doc32[] PROGMEM = "(or item*)\n" -"Evaluates its arguments until one returns non-nil, and returns its value."; -const char doc33[] PROGMEM = "(setq symbol value [symbol value]*)\n" -"For each pair of arguments assigns the value of the second argument\n" -"to the variable specified in the first argument."; -const char doc34[] PROGMEM = "(loop forms*)\n" -"Executes its arguments repeatedly until one of the arguments calls (return),\n" -"which then causes an exit from the loop."; -const char doc35[] PROGMEM = "(push item place)\n" -"Modifies the value of place, which should be a list, to add item onto the front of the list,\n" -"and returns the new list."; -const char doc36[] PROGMEM = "(pop place)\n" -"Modifies the value of place, which should be a non-nil list, to remove its first item,\n" -"and returns that item."; -const char doc37[] PROGMEM = "(incf place [number])\n" -"Increments a place, which should have an numeric value, and returns the result.\n" -"The third argument is an optional increment which defaults to 1."; -const char doc38[] PROGMEM = "(decf place [number])\n" -"Decrements a place, which should have an numeric value, and returns the result.\n" -"The third argument is an optional decrement which defaults to 1."; -const char doc39[] PROGMEM = "(setf place value [place value]*)\n" -"For each pair of arguments modifies a place to the result of evaluating value."; -const char doc40[] PROGMEM = "(dolist (var list [result]) form*)\n" -"Sets the local variable var to each element of list in turn, and executes the forms.\n" -"It then returns result, or nil if result is omitted."; -const char doc41[] PROGMEM = "(dotimes (var number [result]) form*)\n" -"Executes the forms number times, with the local variable var set to each integer from 0 to number-1 in turn.\n" -"It then returns result, or nil if result is omitted."; -const char doc42[] PROGMEM = "(do ((var [init [step]])*) (end-test result*) form*)\n" -"Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step sequentially.\n" -"The forms are executed until end-test is true. It returns result."; -const char doc43[] PROGMEM = "(do* ((var [init [step]])*) (end-test result*) form*)\n" -"Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step in parallel.\n" -"The forms are executed until end-test is true. It returns result."; -const char doc44[] PROGMEM = "(trace [function]*)\n" -"Turns on tracing of up to TRACEMAX user-defined functions,\n" -"and returns a list of the functions currently being traced."; -const char doc45[] PROGMEM = "(untrace [function]*)\n" -"Turns off tracing of up to TRACEMAX user-defined functions, and returns a list of the functions untraced.\n" -"If no functions are specified it untraces all functions."; -const char doc46[] PROGMEM = "(for-millis ([number]) form*)\n" -"Executes the forms and then waits until a total of number milliseconds have elapsed.\n" -"Returns the total number of milliseconds taken."; -const char doc47[] PROGMEM = "(time form)\n" -"Prints the value returned by the form, and the time taken to evaluate the form\n" -"in milliseconds or seconds."; -const char doc48[] PROGMEM = "(with-output-to-string (str) form*)\n" -"Returns a string containing the output to the stream variable str."; -const char doc49[] PROGMEM = "(with-serial (str port [baud]) form*)\n" -"Evaluates the forms with str bound to a serial-stream using port.\n" -"The optional baud gives the baud rate divided by 100, default 96."; -const char doc50[] PROGMEM = "(with-i2c (str [port] address [read-p]) form*)\n" -"Evaluates the forms with str bound to an i2c-stream defined by address.\n" -"If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes\n" -"to be read from the stream. If port is omitted it defaults to 0, otherwise it specifies the port, 0 or 1."; -const char doc51[] PROGMEM = "(with-sd-card (str filename [mode]) form*)\n" -"Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename.\n" -"If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite."; -const char doc52[] PROGMEM = "(progn form*)\n" -"Evaluates several forms grouped together into a block, and returns the result of evaluating the last form."; -const char doc53[] PROGMEM = "(if test then [else])\n" -"Evaluates test. If it's non-nil the form then is evaluated and returned;\n" -"otherwise the form else is evaluated and returned."; -const char doc54[] PROGMEM = "(cond ((test form*) (test form*) ... ))\n" -"Each argument is a list consisting of a test optionally followed by one or more forms.\n" -"If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond.\n" -"If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way."; -const char doc55[] PROGMEM = "(when test form*)\n" -"Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned."; -const char doc56[] PROGMEM = "(unless test form*)\n" -"Evaluates the test. If it's nil the forms are evaluated and the last value is returned."; -const char doc57[] PROGMEM = "(case keyform ((key form*) (key form*) ... ))\n" -"Evaluates a keyform to produce a test key, and then tests this against a series of arguments,\n" -"each of which is a list containing a key optionally followed by one or more forms."; -const char doc58[] PROGMEM = "(and item*)\n" -"Evaluates its arguments until one returns nil, and returns the last value."; -const char doc59[] PROGMEM = "(not item)\n" -"Returns t if its argument is nil, or nil otherwise. Equivalent to null."; -const char doc61[] PROGMEM = "(cons item item)\n" -"If the second argument is a list, cons returns a new list with item added to the front of the list.\n" -"If the second argument isn't a list cons returns a dotted pair."; -const char doc62[] PROGMEM = "(atom item)\n" -"Returns t if its argument is a single number, symbol, or nil."; -const char doc63[] PROGMEM = "(listp item)\n" -"Returns t if its argument is a list."; -const char doc64[] PROGMEM = "(consp item)\n" -"Returns t if its argument is a non-null list."; -const char doc65[] PROGMEM = "(symbolp item)\n" -"Returns t if its argument is a symbol."; -const char doc66[] PROGMEM = "(arrayp item)\n" -"Returns t if its argument is an array."; -const char doc67[] PROGMEM = "(boundp item)\n" -"Returns t if its argument is a symbol with a value."; -const char doc68[] PROGMEM = "(keywordp item)\n" -"Returns t if its argument is a built-in or user-defined keyword."; -const char doc69[] PROGMEM = "(set symbol value [symbol value]*)\n" -"For each pair of arguments, assigns the value of the second argument to the value of the first argument."; -const char doc70[] PROGMEM = "(streamp item)\n" -"Returns t if its argument is a stream."; -const char doc71[] PROGMEM = "(equal item item)\n" -"Tests whether the two arguments are the same symbol, same character, equal numbers,\n" -"or point to the same cons, and returns t or nil as appropriate."; -const char doc72[] PROGMEM = "(caar list)"; -const char doc73[] PROGMEM = "(cadr list)"; -const char doc75[] PROGMEM = "(cdar list)\n" -"Equivalent to (cdr (car list))."; -const char doc76[] PROGMEM = "(cddr list)\n" -"Equivalent to (cdr (cdr list))."; -const char doc77[] PROGMEM = "(caaar list)\n" -"Equivalent to (car (car (car list)))."; -const char doc78[] PROGMEM = "(caadr list)\n" -"Equivalent to (car (car (cdar list)))."; -const char doc79[] PROGMEM = "(cadar list)\n" -"Equivalent to (car (cdr (car list)))."; -const char doc80[] PROGMEM = "(caddr list)\n" -"Equivalent to (car (cdr (cdr list)))."; -const char doc82[] PROGMEM = "(cdaar list)\n" -"Equivalent to (cdar (car (car list)))."; -const char doc83[] PROGMEM = "(cdadr list)\n" -"Equivalent to (cdr (car (cdr list)))."; -const char doc84[] PROGMEM = "(cddar list)\n" -"Equivalent to (cdr (cdr (car list)))."; -const char doc85[] PROGMEM = "(cdddr list)\n" -"Equivalent to (cdr (cdr (cdr list)))."; -const char doc86[] PROGMEM = "(length item)\n" -"Returns the number of items in a list, the length of a string, or the length of a one-dimensional array."; -const char doc87[] PROGMEM = "(array-dimensions item)\n" -"Returns a list of the dimensions of an array."; -const char doc88[] PROGMEM = "(list item*)\n" -"Returns a list of the values of its arguments."; -const char doc89[] PROGMEM = "(copy-list list)\n" -"Returns a copy of a list."; -const char doc90[] PROGMEM = "(make-array size [:initial-element element] [:element-type 'bit])\n" -"If size is an integer it creates a one-dimensional array with elements from 0 to size-1.\n" -"If size is a list of n integers it creates an n-dimensional array with those dimensions.\n" -"If :element-type 'bit is specified the array is a bit array."; -const char doc91[] PROGMEM = "(reverse list)\n" -"Returns a list with the elements of list in reverse order."; -const char doc92[] PROGMEM = "(assoc key list [:test function])\n" -"Looks up a key in an association list of (key . value) pairs, using eq or the specified test function,\n" -"and returns the matching pair, or nil if no pair is found."; -const char doc93[] PROGMEM = "(member item list [:test function])\n" -"Searches for an item in a list, using eq or the specified test function, and returns the list starting\n" -"from the first occurrence of the item, or nil if it is not found."; -const char doc94[] PROGMEM = "(apply function list)\n" -"Returns the result of evaluating function, with the list of arguments specified by the second parameter."; -const char doc95[] PROGMEM = "(funcall function argument*)\n" -"Evaluates function with the specified arguments."; -const char doc96[] PROGMEM = "(append list*)\n" -"Joins its arguments, which should be lists, into a single list."; -const char doc97[] PROGMEM = "(mapc function list1 [list]*)\n" -"Applies the function to each element in one or more lists, ignoring the results.\n" -"It returns the first list argument."; -const char doc98[] PROGMEM = "(mapl function list1 [list]*)\n" -"Applies the function to one or more lists and then successive cdrs of those lists,\n" -"ignoring the results. It returns the first list argument."; -const char doc99[] PROGMEM = "(mapcar function list1 [list]*)\n" -"Applies the function to each element in one or more lists, and returns the resulting list."; -const char doc100[] PROGMEM = "(mapcan function list1 [list]*)\n" -"Applies the function to each element in one or more lists. The results should be lists,\n" -"and these are destructively concatenated together to give the value returned."; -const char doc101[] PROGMEM = "(maplist function list1 [list]*)\n" -"Applies the function to one or more lists and then successive cdrs of those lists,\n" -"and returns the resulting list."; -const char doc102[] PROGMEM = "(mapcon function list1 [list]*)\n" -"Applies the function to one or more lists and then successive cdrs of those lists,\n" -"and these are destructively concatenated together to give the value returned."; -const char doc103[] PROGMEM = "(+ number*)\n" -"Adds its arguments together.\n" -"If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n" -"otherwise a floating-point number."; -const char doc104[] PROGMEM = "(- number*)\n" -"If there is one argument, negates the argument.\n" -"If there are two or more arguments, subtracts the second and subsequent arguments from the first argument.\n" -"If each argument is an integer, and the running total doesn't overflow, returns the result as an integer,\n" -"otherwise a floating-point number."; -const char doc105[] PROGMEM = "(* number*)\n" -"Multiplies its arguments together.\n" -"If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n" -"otherwise it's a floating-point number."; -const char doc106[] PROGMEM = "(/ number*)\n" -"Divides the first argument by the second and subsequent arguments.\n" -"If each argument is an integer, and each division produces an exact result, the result is an integer;\n" -"otherwise it's a floating-point number."; -const char doc107[] PROGMEM = "(mod number number)\n" -"Returns its first argument modulo the second argument.\n" -"If both arguments are integers the result is an integer; otherwise it's a floating-point number."; -const char doc108[] PROGMEM = "(1+ number)\n" -"Adds one to its argument and returns it.\n" -"If the argument is an integer the result is an integer if possible;\n" -"otherwise it's a floating-point number."; -const char doc109[] PROGMEM = "(1- number)\n" -"Subtracts one from its argument and returns it.\n" -"If the argument is an integer the result is an integer if possible;\n" -"otherwise it's a floating-point number."; -const char doc110[] PROGMEM = "(abs number)\n" -"Returns the absolute, positive value of its argument.\n" -"If the argument is an integer the result will be returned as an integer if possible,\n" -"otherwise a floating-point number."; -const char doc111[] PROGMEM = "(random number)\n" -"If number is an integer returns a random number between 0 and one less than its argument.\n" -"Otherwise returns a floating-point number between zero and number."; -const char doc112[] PROGMEM = "(max number*)\n" -"Returns the maximum of one or more arguments."; -const char doc113[] PROGMEM = "(min number*)\n" -"Returns the minimum of one or more arguments."; -const char doc114[] PROGMEM = "(/= number*)\n" -"Returns t if none of the arguments are equal, or nil if two or more arguments are equal."; -const char doc115[] PROGMEM = "(= number*)\n" -"Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise."; -const char doc116[] PROGMEM = "(< number*)\n" -"Returns t if each argument is less than the next argument, and nil otherwise."; -const char doc117[] PROGMEM = "(<= number*)\n" -"Returns t if each argument is less than or equal to the next argument, and nil otherwise."; -const char doc118[] PROGMEM = "(> number*)\n" -"Returns t if each argument is greater than the next argument, and nil otherwise."; -const char doc119[] PROGMEM = "(>= number*)\n" -"Returns t if each argument is greater than or equal to the next argument, and nil otherwise."; -const char doc120[] PROGMEM = "(plusp number)\n" -"Returns t if the argument is greater than zero, or nil otherwise."; -const char doc121[] PROGMEM = "(minusp number)\n" -"Returns t if the argument is less than zero, or nil otherwise."; -const char doc122[] PROGMEM = "(zerop number)\n" -"Returns t if the argument is zero."; -const char doc123[] PROGMEM = "(oddp number)\n" -"Returns t if the integer argument is odd."; -const char doc124[] PROGMEM = "(evenp number)\n" -"Returns t if the integer argument is even."; -const char doc125[] PROGMEM = "(integerp number)\n" -"Returns t if the argument is an integer."; -const char doc126[] PROGMEM = "(numberp number)\n" -"Returns t if the argument is a number."; -const char doc127[] PROGMEM = "(float number)\n" -"Returns its argument converted to a floating-point number."; -const char doc128[] PROGMEM = "(floatp number)\n" -"Returns t if the argument is a floating-point number."; -const char doc129[] PROGMEM = "(sin number)\n" -"Returns sin(number)."; -const char doc130[] PROGMEM = "(cos number)\n" -"Returns cos(number)."; -const char doc131[] PROGMEM = "(tan number)\n" -"Returns tan(number)."; -const char doc132[] PROGMEM = "(asin number)\n" -"Returns asin(number)."; -const char doc133[] PROGMEM = "(acos number)\n" -"Returns acos(number)."; -const char doc134[] PROGMEM = "(atan number1 [number2])\n" -"Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1."; -const char doc135[] PROGMEM = "(sinh number)\n" -"Returns sinh(number)."; -const char doc136[] PROGMEM = "(cosh number)\n" -"Returns cosh(number)."; -const char doc137[] PROGMEM = "(tanh number)\n" -"Returns tanh(number)."; -const char doc138[] PROGMEM = "(exp number)\n" -"Returns exp(number)."; -const char doc139[] PROGMEM = "(sqrt number)\n" -"Returns sqrt(number)."; -const char doc140[] PROGMEM = "(log number [base])\n" -"Returns the logarithm of number to the specified base. If base is omitted it defaults to e."; -const char doc141[] PROGMEM = "(expt number power)\n" -"Returns number raised to the specified power.\n" -"Returns the result as an integer if the arguments are integers and the result will be within range,\n" -"otherwise a floating-point number."; -const char doc142[] PROGMEM = "(ceiling number [divisor])\n" -"Returns ceil(number/divisor). If omitted, divisor is 1."; -const char doc143[] PROGMEM = "(floor number [divisor])\n" -"Returns floor(number/divisor). If omitted, divisor is 1."; -const char doc144[] PROGMEM = "(truncate number [divisor])\n" -"Returns the integer part of number/divisor. If divisor is omitted it defaults to 1."; -const char doc145[] PROGMEM = "(round number [divisor])\n" -"Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1."; -const char doc146[] PROGMEM = "(char-code character)\n" -"Returns the ASCII code for a character, as an integer."; -const char doc147[] PROGMEM = "(code-char integer)\n" -"Returns the character for the specified ASCII code."; -const char doc148[] PROGMEM = "(characterp item)\n" -"Returns t if the argument is a character and nil otherwise."; -const char doc149[] PROGMEM = "(stringp item)\n" -"Returns t if the argument is a string and nil otherwise."; -const char doc150[] PROGMEM = "(string= string string)\n" -"Returns t if the two strings are the same, or nil otherwise."; -const char doc151[] PROGMEM = "(string< string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically less than the second string,\n" -"or nil otherwise."; -const char doc152[] PROGMEM = "(string> string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically greater than the second string,\n" -"or nil otherwise."; -const char doc153[] PROGMEM = "(string/= string string)\n" -"Returns the index to the first mismatch if the two strings are not the same, or nil otherwise."; -const char doc154[] PROGMEM = "(string<= string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically less than or equal to\n" -"the second string, or nil otherwise."; -const char doc155[] PROGMEM = "(string>= string string)\n" -"Returns the index to the first mismatch if the first string is alphabetically greater than or equal to\n" -"the second string, or nil otherwise."; -const char doc156[] PROGMEM = "(sort list test)\n" -"Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list."; -const char doc157[] PROGMEM = "(concatenate 'string string*)\n" -"Joins together the strings given in the second and subsequent arguments, and returns a single string."; -const char doc158[] PROGMEM = "(subseq seq start [end])\n" -"Returns a subsequence of a list or string from item start to item end-1."; -const char doc159[] PROGMEM = "(search pattern target [:test function])\n" -"Returns the index of the first occurrence of pattern in target, or nil if it's not found.\n" -"The target can be a list or string. If it's a list a test function can be specified; default eq."; -const char doc160[] PROGMEM = "(read-from-string string)\n" -"Reads an atom or list from the specified string and returns it."; -const char doc161[] PROGMEM = "(princ-to-string item)\n" -"Prints its argument to a string, and returns the string.\n" -"Characters and strings are printed without quotation marks or escape characters."; -const char doc162[] PROGMEM = "(prin1-to-string item [stream])\n" -"Prints its argument to a string, and returns the string.\n" -"Characters and strings are printed with quotation marks and escape characters,\n" -"in a format that will be suitable for read-from-string."; -const char doc163[] PROGMEM = "(logand [value*])\n" -"Returns the bitwise & of the values."; -const char doc164[] PROGMEM = "(logior [value*])\n" -"Returns the bitwise | of the values."; -const char doc165[] PROGMEM = "(logxor [value*])\n" -"Returns the bitwise ^ of the values."; -const char doc166[] PROGMEM = "(lognot value)\n" -"Returns the bitwise logical NOT of the value."; -const char doc167[] PROGMEM = "(ash value shift)\n" -"Returns the result of bitwise shifting value by shift bits. If shift is positive, value is shifted to the left."; -const char doc168[] PROGMEM = "(logbitp bit value)\n" -"Returns t if bit number bit in value is a '1', and nil if it is a '0'."; -const char doc169[] PROGMEM = "(eval form*)\n" -"Evaluates its argument an extra time."; -const char doc170[] PROGMEM = "(return [value])\n" -"Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value."; -const char doc171[] PROGMEM = "(globals)\n" -"Returns a list of global variables."; -const char doc172[] PROGMEM = "(locals)\n" -"Returns an association list of local variables and their values."; -const char doc173[] PROGMEM = "(makunbound symbol)\n" -"Removes the value of the symbol from GlobalEnv and returns the symbol."; -const char doc174[] PROGMEM = "(break)\n" -"Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL."; -const char doc175[] PROGMEM = "(read [stream])\n" -"Reads an atom or list from the serial input and returns it.\n" -"If stream is specified the item is read from the specified stream."; -const char doc176[] PROGMEM = "(prin1 item [stream])\n" -"Prints its argument, and returns its value.\n" -"Strings are printed with quotation marks and escape characters."; -const char doc177[] PROGMEM = "(print item [stream])\n" -"Prints its argument with quotation marks and escape characters, on a new line, and followed by a space.\n" -"If stream is specified the argument is printed to the specified stream."; -const char doc178[] PROGMEM = "(princ item [stream])\n" -"Prints its argument, and returns its value.\n" -"Characters and strings are printed without quotation marks or escape characters."; -const char doc179[] PROGMEM = "(terpri [stream])\n" -"Prints a new line, and returns nil.\n" -"If stream is specified the new line is written to the specified stream."; -const char doc180[] PROGMEM = "(read-byte stream)\n" -"Reads a byte from a stream and returns it."; -const char doc181[] PROGMEM = "(read-line [stream])\n" -"Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline.\n" -"If stream is specified the line is read from the specified stream."; -const char doc182[] PROGMEM = "(write-byte number [stream])\n" -"Writes a byte to a stream."; -const char doc183[] PROGMEM = "(write-string string [stream])\n" -"Writes a string. If stream is specified the string is written to the stream."; -const char doc184[] PROGMEM = "(write-line string [stream])\n" -"Writes a string terminated by a newline character. If stream is specified the string is written to the stream."; -const char doc185[] PROGMEM = "(restart-i2c stream [read-p])\n" -"Restarts an i2c-stream.\n" -"If read-p is nil or omitted the stream is written to.\n" -"If read-p is an integer it specifies the number of bytes to be read from the stream."; -const char doc186[] PROGMEM = "(gc)\n" -"Forces a garbage collection and prints the number of objects collected, and the time taken."; -const char doc187[] PROGMEM = "(room)\n" -"Returns the number of free Lisp cells remaining."; -const char doc188[] PROGMEM = "(save-image [symbol])\n" -"Saves the current uLisp image to non-volatile memory or SD card so it can be loaded using load-image."; -const char doc189[] PROGMEM = "(load-image [filename])\n" -"Loads a saved uLisp image from non-volatile memory or SD card."; -const char doc190[] PROGMEM = "(cls)\n" -"Prints a clear-screen character."; -const char doc191[] PROGMEM = "(digitalread pin)\n" -"Reads the state of the specified Arduino pin number and returns t (high) or nil (low)."; -const char doc192[] PROGMEM = "(analogreadresolution bits)\n" -"Specifies the resolution for the analogue inputs on platforms that support it.\n" -"The default resolution on all platforms is 10 bits."; -const char doc193[] PROGMEM = "(analogwrite pin value)\n" -"Writes the value to the specified Arduino pin number."; -const char doc194[] PROGMEM = "(delay number)\n" -"Delays for a specified number of milliseconds."; -const char doc195[] PROGMEM = "(millis)\n" -"Returns the time in milliseconds that uLisp has been running."; -const char doc196[] PROGMEM = "(sleep secs)\n" -"Puts the processor into a low-power sleep mode for secs.\n" -"Only supported on some platforms. On other platforms it does delay(1000*secs)."; -const char doc197[] PROGMEM = "(note [pin] [note] [octave])\n" -"Generates a square wave on pin.\n" -"note represents the note in the well-tempered scale.\n" -"The argument octave can specify an octave; default 0."; -const char doc198[] PROGMEM = "(edit 'function)\n" -"Calls the Lisp tree editor to allow you to edit a function definition."; -const char doc199[] PROGMEM = "(pprint item [str])\n" -"Prints its argument, using the pretty printer, to display it formatted in a structured way.\n" -"If str is specified it prints to the specified stream. It returns no value."; -const char doc200[] PROGMEM = "(pprintall [str])\n" -"Pretty-prints the definition of every function and variable defined in the uLisp workspace.\n" -"If str is specified it prints to the specified stream. It returns no value."; -const char doc201[] PROGMEM = "(require 'symbol)\n" -"Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library.\n" -"It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library."; -const char doc202[] PROGMEM = "(list-library)\n" -"Prints a list of the functions defined in the List Library."; -const char doc203[] PROGMEM = "(? item)\n" -"Prints the documentation string of a built-in or user-defined function."; -const char doc204[] PROGMEM = "(documentation 'symbol [type])\n" -"Returns the documentation string of a built-in or user-defined function. The type argument is ignored."; -const char doc205[] PROGMEM = "(apropos item)\n" -"Prints the user-defined and built-in functions whose names contain the specified string or symbol."; -const char doc206[] PROGMEM = "(apropos-list item)\n" -"Returns a list of user-defined and built-in functions whose names contain the specified string or symbol."; -const char doc207[] PROGMEM = "(unwind-protect form1 [forms]*)\n" -"Evaluates form1 and forms in order and returns the value of form1,\n" -"but guarantees to evaluate forms even if an error occurs in form1."; -const char doc208[] PROGMEM = "(ignore-errors [forms]*)\n" -"Evaluates forms ignoring errors."; -const char doc209[] PROGMEM = "(error controlstring [arguments]*)\n" -"Signals an error. The message is printed by format using the controlstring and arguments."; -const char doc210[] PROGMEM = "(with-client (str [address port]) form*)\n" -"Evaluates the forms with str bound to a wifi-stream."; -const char doc211[] PROGMEM = "(available stream)\n" -"Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available."; -const char doc212[] PROGMEM = "(wifi-server)\n" -"Starts a Wi-Fi server running. It returns nil."; -const char doc213[] PROGMEM = "(wifi-softap ssid [password channel hidden])\n" -"Set up a soft access point to establish a Wi-Fi network.\n" -"Returns the IP address as a string or nil if unsuccessful."; -const char doc214[] PROGMEM = "(connected stream)\n" -"Returns t or nil to indicate if the client on stream is connected."; -const char doc215[] PROGMEM = "(wifi-localip)\n" -"Returns the IP address of the local network as a string."; -const char doc216[] PROGMEM = "(wifi-connect [ssid pass])\n" -"Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string."; -const char doc217[] PROGMEM = "(with-gfx (str) form*)\n" -"Evaluates the forms with str bound to an gfx-stream so you can print text\n" -"to the graphics display using the standard uLisp print commands."; -const char doc218[] PROGMEM = "(draw-pixel x y [colour])\n" -"Draws a pixel at coordinates (x,y) in colour, or white if omitted."; -const char doc219[] PROGMEM = "(draw-line x0 y0 x1 y1 [colour])\n" -"Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted."; -const char doc220[] PROGMEM = "(draw-rect x y w h [colour])\n" -"Draws an outline rectangle with its top left corner at (x,y), with width w,\n" -"and with height h. The outline is drawn in colour, or white if omitted."; -const char doc221[] PROGMEM = "(fill-rect x y w h [colour])\n" -"Draws a filled rectangle with its top left corner at (x,y), with width w,\n" -"and with height h. The outline is drawn in colour, or white if omitted."; -const char doc222[] PROGMEM = "(draw-circle x y r [colour])\n" -"Draws an outline circle with its centre at (x, y) and with radius r.\n" -"The circle is drawn in colour, or white if omitted."; -const char doc223[] PROGMEM = "(fill-circle x y r [colour])\n" -"Draws a filled circle with its centre at (x, y) and with radius r.\n" -"The circle is drawn in colour, or white if omitted."; -const char doc224[] PROGMEM = "(draw-round-rect x y w h radius [colour])\n" -"Draws an outline rounded rectangle with its top left corner at (x,y), with width w,\n" -"height h, and corner radius radius. The outline is drawn in colour, or white if omitted."; -const char doc225[] PROGMEM = "(fill-round-rect x y w h radius [colour])\n" -"Draws a filled rounded rectangle with its top left corner at (x,y), with width w,\n" -"height h, and corner radius radius. The outline is drawn in colour, or white if omitted."; -const char doc226[] PROGMEM = "(draw-triangle x0 y0 x1 y1 x2 y2 [colour])\n" -"Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3).\n" -"The outline is drawn in colour, or white if omitted."; -const char doc227[] PROGMEM = "(fill-triangle x0 y0 x1 y1 x2 y2 [colour])\n" -"Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3).\n" -"The outline is drawn in colour, or white if omitted."; -const char doc228[] PROGMEM = "(draw-char x y char [colour background size])\n" -"Draws the character char with its top left corner at (x,y).\n" -"The character is drawn in a 5 x 7 pixel font in colour against background,\n" -"which default to white and black respectively.\n" -"The character can optionally be scaled by size."; -const char doc229[] PROGMEM = "(set-cursor x y)\n" -"Sets the start point for text plotting to (x, y)."; -const char doc230[] PROGMEM = "(set-text-color colour [background])\n" -"Sets the text colour for text plotted using (with-gfx ...)."; -const char doc231[] PROGMEM = "(set-text-size scale)\n" -"Scales text by the specified size, default 1."; -const char doc232[] PROGMEM = "(set-text-wrap boolean)\n" -"Specified whether text wraps at the right-hand edge of the display; the default is t."; -const char doc233[] PROGMEM = "(fill-screen [colour])\n" -"Fills or clears the screen with colour, default black."; -const char doc234[] PROGMEM = "(set-rotation option)\n" -"Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3."; -const char doc235[] PROGMEM = "(invert-display boolean)\n" -"Mirror-images the display."; - -// Built-in symbol lookup table -const tbl_entry_t lookup_table[] PROGMEM = { - { string0, NULL, 0000, doc0 }, - { string1, NULL, 0000, doc1 }, - { string2, NULL, 0000, doc2 }, - { string3, NULL, 0000, doc3 }, - { string4, NULL, 0000, doc4 }, - { string5, NULL, 0000, NULL }, - { string6, NULL, 0000, NULL }, - { string7, NULL, 0000, NULL }, - { string8, NULL, 0000, NULL }, - { string9, NULL, 0000, doc9 }, - { string10, NULL, 0017, doc10 }, - { string11, NULL, 0017, doc11 }, - { string12, NULL, 0017, doc12 }, - { string13, NULL, 0017, NULL }, - { string14, NULL, 0007, NULL }, - { string15, sp_quote, 0311, NULL }, - { string16, sp_defun, 0327, doc16 }, - { string17, sp_defvar, 0313, doc17 }, - { string18, fn_eq, 0222, doc18 }, - { string19, fn_car, 0211, doc19 }, - { string20, fn_car, 0211, NULL }, - { string21, fn_cdr, 0211, doc21 }, - { string22, fn_cdr, 0211, NULL }, - { string23, fn_nth, 0222, doc23 }, - { string24, fn_aref, 0227, doc24 }, - { string25, fn_char, 0222, doc25 }, - { string26, fn_stringfn, 0211, doc26 }, - { string27, fn_pinmode, 0222, doc27 }, - { string28, fn_digitalwrite, 0222, doc28 }, - { string29, fn_analogread, 0211, doc29 }, - { string30, fn_register, 0212, doc30 }, - { string31, fn_format, 0227, doc31 }, - { string32, sp_or, 0307, doc32 }, - { string33, sp_setq, 0327, doc33 }, - { string34, sp_loop, 0307, doc34 }, - { string35, sp_push, 0322, doc35 }, - { string36, sp_pop, 0311, doc36 }, - { string37, sp_incf, 0312, doc37 }, - { string38, sp_decf, 0312, doc38 }, - { string39, sp_setf, 0327, doc39 }, - { string40, sp_dolist, 0317, doc40 }, - { string41, sp_dotimes, 0317, doc41 }, - { string42, sp_do, 0327, doc42 }, - { string43, sp_dostar, 0317, doc43 }, - { string44, sp_trace, 0301, doc44 }, - { string45, sp_untrace, 0301, doc45 }, - { string46, sp_formillis, 0317, doc46 }, - { string47, sp_time, 0311, doc47 }, - { string48, sp_withoutputtostring, 0317, doc48 }, - { string49, sp_withserial, 0317, doc49 }, - { string50, sp_withi2c, 0317, doc50 }, - { string51, sp_withsdcard, 0327, doc51 }, - { string52, tf_progn, 0107, doc52 }, - { string53, tf_if, 0123, doc53 }, - { string54, tf_cond, 0107, doc54 }, - { string55, tf_when, 0117, doc55 }, - { string56, tf_unless, 0117, doc56 }, - { string57, tf_case, 0117, doc57 }, - { string58, tf_and, 0107, doc58 }, - { string59, fn_not, 0211, doc59 }, - { string60, fn_not, 0211, NULL }, - { string61, fn_cons, 0222, doc61 }, - { string62, fn_atom, 0211, doc62 }, - { string63, fn_listp, 0211, doc63 }, - { string64, fn_consp, 0211, doc64 }, - { string65, fn_symbolp, 0211, doc65 }, - { string66, fn_arrayp, 0211, doc66 }, - { string67, fn_boundp, 0211, doc67 }, - { string68, fn_keywordp, 0211, doc68 }, - { string69, fn_setfn, 0227, doc69 }, - { string70, fn_streamp, 0211, doc70 }, - { string71, fn_equal, 0222, doc71 }, - { string72, fn_caar, 0211, doc72 }, - { string73, fn_cadr, 0211, doc73 }, - { string74, fn_cadr, 0211, NULL }, - { string75, fn_cdar, 0211, doc75 }, - { string76, fn_cddr, 0211, doc76 }, - { string77, fn_caaar, 0211, doc77 }, - { string78, fn_caadr, 0211, doc78 }, - { string79, fn_cadar, 0211, doc79 }, - { string80, fn_caddr, 0211, doc80 }, - { string81, fn_caddr, 0211, NULL }, - { string82, fn_cdaar, 0211, doc82 }, - { string83, fn_cdadr, 0211, doc83 }, - { string84, fn_cddar, 0211, doc84 }, - { string85, fn_cdddr, 0211, doc85 }, - { string86, fn_length, 0211, doc86 }, - { string87, fn_arraydimensions, 0211, doc87 }, - { string88, fn_list, 0207, doc88 }, - { string89, fn_copylist, 0211, doc89 }, - { string90, fn_makearray, 0215, doc90 }, - { string91, fn_reverse, 0211, doc91 }, - { string92, fn_assoc, 0224, doc92 }, - { string93, fn_member, 0224, doc93 }, - { string94, fn_apply, 0227, doc94 }, - { string95, fn_funcall, 0217, doc95 }, - { string96, fn_append, 0207, doc96 }, - { string97, fn_mapc, 0227, doc97 }, - { string98, fn_mapl, 0227, doc98 }, - { string99, fn_mapcar, 0227, doc99 }, - { string100, fn_mapcan, 0227, doc100 }, - { string101, fn_maplist, 0227, doc101 }, - { string102, fn_mapcon, 0227, doc102 }, - { string103, fn_add, 0207, doc103 }, - { string104, fn_subtract, 0217, doc104 }, - { string105, fn_multiply, 0207, doc105 }, - { string106, fn_divide, 0217, doc106 }, - { string107, fn_mod, 0222, doc107 }, - { string108, fn_oneplus, 0211, doc108 }, - { string109, fn_oneminus, 0211, doc109 }, - { string110, fn_abs, 0211, doc110 }, - { string111, fn_random, 0211, doc111 }, - { string112, fn_maxfn, 0217, doc112 }, - { string113, fn_minfn, 0217, doc113 }, - { string114, fn_noteq, 0217, doc114 }, - { string115, fn_numeq, 0217, doc115 }, - { string116, fn_less, 0217, doc116 }, - { string117, fn_lesseq, 0217, doc117 }, - { string118, fn_greater, 0217, doc118 }, - { string119, fn_greatereq, 0217, doc119 }, - { string120, fn_plusp, 0211, doc120 }, - { string121, fn_minusp, 0211, doc121 }, - { string122, fn_zerop, 0211, doc122 }, - { string123, fn_oddp, 0211, doc123 }, - { string124, fn_evenp, 0211, doc124 }, - { string125, fn_integerp, 0211, doc125 }, - { string126, fn_numberp, 0211, doc126 }, - { string127, fn_floatfn, 0211, doc127 }, - { string128, fn_floatp, 0211, doc128 }, - { string129, fn_sin, 0211, doc129 }, - { string130, fn_cos, 0211, doc130 }, - { string131, fn_tan, 0211, doc131 }, - { string132, fn_asin, 0211, doc132 }, - { string133, fn_acos, 0211, doc133 }, - { string134, fn_atan, 0212, doc134 }, - { string135, fn_sinh, 0211, doc135 }, - { string136, fn_cosh, 0211, doc136 }, - { string137, fn_tanh, 0211, doc137 }, - { string138, fn_exp, 0211, doc138 }, - { string139, fn_sqrt, 0211, doc139 }, - { string140, fn_log, 0212, doc140 }, - { string141, fn_expt, 0222, doc141 }, - { string142, fn_ceiling, 0212, doc142 }, - { string143, fn_floor, 0212, doc143 }, - { string144, fn_truncate, 0212, doc144 }, - { string145, fn_round, 0212, doc145 }, - { string146, fn_charcode, 0211, doc146 }, - { string147, fn_codechar, 0211, doc147 }, - { string148, fn_characterp, 0211, doc148 }, - { string149, fn_stringp, 0211, doc149 }, - { string150, fn_stringeq, 0222, doc150 }, - { string151, fn_stringless, 0222, doc151 }, - { string152, fn_stringgreater, 0222, doc152 }, - { string153, fn_stringnoteq, 0222, doc153 }, - { string154, fn_stringlesseq, 0222, doc154 }, - { string155, fn_stringgreatereq, 0222, doc155 }, - { string156, fn_sort, 0222, doc156 }, - { string157, fn_concatenate, 0217, doc157 }, - { string158, fn_subseq, 0223, doc158 }, - { string159, fn_search, 0224, doc159 }, - { string160, fn_readfromstring, 0211, doc160 }, - { string161, fn_princtostring, 0211, doc161 }, - { string162, fn_prin1tostring, 0211, doc162 }, - { string163, fn_logand, 0207, doc163 }, - { string164, fn_logior, 0207, doc164 }, - { string165, fn_logxor, 0207, doc165 }, - { string166, fn_lognot, 0211, doc166 }, - { string167, fn_ash, 0222, doc167 }, - { string168, fn_logbitp, 0222, doc168 }, - { string169, fn_eval, 0211, doc169 }, - { string170, fn_return, 0201, doc170 }, - { string171, fn_globals, 0200, doc171 }, - { string172, fn_locals, 0200, doc172 }, - { string173, fn_makunbound, 0211, doc173 }, - { string174, fn_break, 0200, doc174 }, - { string175, fn_read, 0201, doc175 }, - { string176, fn_prin1, 0212, doc176 }, - { string177, fn_print, 0212, doc177 }, - { string178, fn_princ, 0212, doc178 }, - { string179, fn_terpri, 0201, doc179 }, - { string180, fn_readbyte, 0202, doc180 }, - { string181, fn_readline, 0201, doc181 }, - { string182, fn_writebyte, 0212, doc182 }, - { string183, fn_writestring, 0212, doc183 }, - { string184, fn_writeline, 0212, doc184 }, - { string185, fn_restarti2c, 0212, doc185 }, - { string186, fn_gc, 0200, doc186 }, - { string187, fn_room, 0200, doc187 }, - { string188, fn_saveimage, 0201, doc188 }, - { string189, fn_loadimage, 0201, doc189 }, - { string190, fn_cls, 0200, doc190 }, - { string191, fn_digitalread, 0211, doc191 }, - { string192, fn_analogreadresolution, 0211, doc192 }, - { string193, fn_analogwrite, 0222, doc193 }, - { string194, fn_delay, 0211, doc194 }, - { string195, fn_millis, 0200, doc195 }, - { string196, fn_sleep, 0201, doc196 }, - { string197, fn_note, 0203, doc197 }, - { string198, fn_edit, 0211, doc198 }, - { string199, fn_pprint, 0212, doc199 }, - { string200, fn_pprintall, 0201, doc200 }, - { string201, fn_require, 0211, doc201 }, - { string202, fn_listlibrary, 0200, doc202 }, - { string203, sp_help, 0311, doc203 }, - { string204, fn_documentation, 0212, doc204 }, - { string205, fn_apropos, 0211, doc205 }, - { string206, fn_aproposlist, 0211, doc206 }, - { string207, sp_unwindprotect, 0307, doc207 }, - { string208, sp_ignoreerrors, 0307, doc208 }, - { string209, sp_error, 0317, doc209 }, - { string210, sp_withclient, 0313, doc210 }, - { string211, fn_available, 0211, doc211 }, - { string212, fn_wifiserver, 0200, doc212 }, - { string213, fn_wifisoftap, 0204, doc213 }, - { string214, fn_connected, 0211, doc214 }, - { string215, fn_wifilocalip, 0200, doc215 }, - { string216, fn_wificonnect, 0203, doc216 }, - { string217, sp_withgfx, 0317, doc217 }, - { string218, fn_drawpixel, 0223, doc218 }, - { string219, fn_drawline, 0245, doc219 }, - { string220, fn_drawrect, 0245, doc220 }, - { string221, fn_fillrect, 0245, doc221 }, - { string222, fn_drawcircle, 0234, doc222 }, - { string223, fn_fillcircle, 0234, doc223 }, - { string224, fn_drawroundrect, 0256, doc224 }, - { string225, fn_fillroundrect, 0256, doc225 }, - { string226, fn_drawtriangle, 0267, doc226 }, - { string227, fn_filltriangle, 0267, doc227 }, - { string228, fn_drawchar, 0236, doc228 }, - { string229, fn_setcursor, 0222, doc229 }, - { string230, fn_settextcolor, 0212, doc230 }, - { string231, fn_settextsize, 0211, doc231 }, - { string232, fn_settextwrap, 0211, doc232 }, - { string233, fn_fillscreen, 0201, doc233 }, - { string234, fn_setrotation, 0211, doc234 }, - { string235, fn_invertdisplay, 0211, doc235 }, - { string236, (fn_ptr_type)LED_BUILTIN, 0, NULL }, - { string237, (fn_ptr_type)HIGH, DIGITALWRITE, NULL }, - { string238, (fn_ptr_type)LOW, DIGITALWRITE, NULL }, - { string239, (fn_ptr_type)INPUT, PINMODE, NULL }, - { string240, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL }, - { string241, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL }, - { string242, (fn_ptr_type)OUTPUT, PINMODE, NULL }, -}; - -#if !defined(extensions) -// Table cross-reference functions - -tbl_entry_t *tables[] = {lookup_table, NULL}; -const unsigned int tablesizes[] = { arraysize(lookup_table), 0 }; - -const tbl_entry_t *table (int n) { - return tables[n]; -} - -unsigned int tablesize (int n) { - return tablesizes[n]; -} -#endif - -// Table lookup functions - -builtin_t lookupbuiltin (char* c) { - unsigned int end = 0, start; - for (int n=0; n<2; n++) { - start = end; - int entries = tablesize(n); - end = end + entries; - for (int i=0; i> 3) & 0x07)) error2(toofewargs); - if ((minmax & 0x07) != 0x07 && nargs>(minmax & 0x07)) error2(toomanyargs); -} - -char *lookupdoc (builtin_t name) { - int n = namechars)>>((sizeof(int)-1)*8) & 0xFF) == ':'); -} - -bool keywordp (object *obj) { - if (!(symbolp(obj) && builtinp(obj->name))) return false; - builtin_t name = builtin(obj->name); - int n = name 4000) { delay(1); start = millis(); } -#endif - // Enough space? - if (Freespace <= WORKSPACESIZE>>4) gc(form, env); - // Escape - if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2("escape!");} - if (!tstflag(NOESC)) testescape(); - - if (form == NULL) return nil; - - if (form->type >= NUMBER && form->type <= STRING) return form; - - if (symbolp(form)) { - symbol_t name = form->name; - if (colonp(name)) return form; // Keyword - object *pair = value(name, env); - if (pair != NULL) return cdr(pair); - pair = value(name, GlobalEnv); - if (pair != NULL) return cdr(pair); - else if (builtinp(name)) { - if (builtin(name) == FEATURES) return features(); - return form; - } - Context = NIL; - error("undefined", form); - } - - // It's a list - object *function = car(form); - object *args = cdr(form); - - if (function == NULL) error("illegal function", nil); - if (!listp(args)) error("can't evaluate a dotted pair", args); - - // List starts with a builtin symbol? - if (symbolp(function) && builtinp(function->name)) { - builtin_t name = builtin(function->name); - - if ((name == LET) || (name == LETSTAR)) { - int TCstart = TC; - if (args == NULL) error2(noargument); - object *assigns = first(args); - if (!listp(assigns)) error(notalist, assigns); - object *forms = cdr(args); - object *newenv = env; - protect(newenv); - while (assigns != NULL) { - object *assign = car(assigns); - if (!consp(assign)) push(cons(assign,nil), newenv); - else if (cdr(assign) == NULL) push(cons(first(assign),nil), newenv); - else push(cons(first(assign),eval(second(assign),env)), newenv); - car(GCStack) = newenv; - if (name == LETSTAR) env = newenv; - assigns = cdr(assigns); - } - env = newenv; - unprotect(); - form = tf_progn(forms,env); - TC = TCstart; - goto EVAL; - } - - if (name == LAMBDA) { - if (env == NULL) return form; - object *envcopy = NULL; - while (env != NULL) { - object *pair = first(env); - if (pair != NULL) push(pair, envcopy); - env = cdr(env); - } - return cons(bsymbol(CLOSURE), cons(envcopy,args)); - } - uint8_t fntype = getminmax(name)>>6; - - if (fntype == SPECIAL_FORMS) { - Context = name; - checkargs(args); - return ((fn_ptr_type)lookupfn(name))(args, env); - } - - if (fntype == TAIL_FORMS) { - Context = name; - checkargs(args); - form = ((fn_ptr_type)lookupfn(name))(args, env); - TC = 1; - goto EVAL; - } - if (fntype == OTHER_FORMS) error("can't be used as a function", function); - } - - // Evaluate the parameters - result in head - object *fname = car(form); - int TCstart = TC; - object *head = cons(eval(fname, env), NULL); - protect(head); // Don't GC the result list - object *tail = head; - form = cdr(form); - int nargs = 0; - - while (form != NULL){ - object *obj = cons(eval(car(form),env),NULL); - cdr(tail) = obj; - tail = obj; - form = cdr(form); - nargs++; - } - - function = car(head); - args = cdr(head); - - if (symbolp(function)) { - builtin_t bname = builtin(function->name); - if (!builtinp(function->name)) error("not valid here", fname); - Context = bname; - checkminmax(bname, nargs); - object *result = ((fn_ptr_type)lookupfn(bname))(args, env); - unprotect(); - return result; - } - - if (consp(function)) { - symbol_t name = sym(NIL); - if (!listp(fname)) name = fname->name; - - if (isbuiltin(car(function), LAMBDA)) { - form = closure(TCstart, name, function, args, &env); - unprotect(); - int trace = tracing(fname->name); - if (trace) { - object *result = eval(form, env); - indent((--(TraceDepth[trace-1]))<<1, ' ', pserial); - pint(TraceDepth[trace-1], pserial); - pserial(':'); pserial(' '); - printobject(fname, pserial); pfstring(" returned ", pserial); - printobject(result, pserial); pln(pserial); - return result; - } else { - TC = 1; - goto EVAL; - } - } - - if (isbuiltin(car(function), CLOSURE)) { - function = cdr(function); - form = closure(TCstart, name, function, args, &env); - unprotect(); - TC = 1; - goto EVAL; - } - - } - error("illegal function", fname); return nil; -} - -// Print functions - -void pserial (char c) { - LastPrint = c; - if (c == '\n') Serial.write('\r'); - Serial.write(c); -} - -const char ControlCodes[] = "Null\0SOH\0STX\0ETX\0EOT\0ENQ\0ACK\0Bell\0Backspace\0Tab\0Newline\0VT\0" -"Page\0Return\0SO\0SI\0DLE\0DC1\0DC2\0DC3\0DC4\0NAK\0SYN\0ETB\0CAN\0EM\0SUB\0Escape\0FS\0GS\0RS\0US\0Space\0"; - -void pcharacter (uint8_t c, pfun_t pfun) { - if (!tstflag(PRINTREADABLY)) pfun(c); - else { - pfun('#'); pfun('\\'); - if (c <= 32) { - const char *p = ControlCodes; - while (c > 0) {p = p + strlen_P(p) + 1; c--; } - pfstring(p, pfun); - } else if (c < 127) pfun(c); - else pint(c, pfun); - } -} - -void pstring (char *s, pfun_t pfun) { - while (*s) pfun(*s++); -} - -void plispstring (object *form, pfun_t pfun) { - plispstr(form->name, pfun); -} - -void plispstr (symbol_t name, pfun_t pfun) { - object *form = (object *)name; - while (form != NULL) { - int chars = form->chars; - for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { - char ch = chars>>i & 0xFF; - if (tstflag(PRINTREADABLY) && (ch == '"' || ch == '\\')) pfun('\\'); - if (ch) pfun(ch); - } - form = car(form); - } -} - -void printstring (object *form, pfun_t pfun) { - if (tstflag(PRINTREADABLY)) pfun('"'); - plispstr(form->name, pfun); - if (tstflag(PRINTREADABLY)) pfun('"'); -} - -void pbuiltin (builtin_t name, pfun_t pfun) { - int n = name0; d = d/40) { - uint32_t j = x/d; - char c = fromradix40(j); - if (c == 0) return; - pfun(c); x = x - j*d; - } -} - -void printsymbol (object *form, pfun_t pfun) { - psymbol(form->name, pfun); -} - -void psymbol (symbol_t name, pfun_t pfun) { - if (longnamep(name)) plispstr(name, pfun); - else { - uint32_t value = untwist(name); - if (value < PACKEDS) error2("invalid symbol"); - else if (value >= BUILTINS) pbuiltin((builtin_t)(value-BUILTINS), pfun); - else pradix40(name, pfun); - } -} - -void pfstring (const char *s, pfun_t pfun) { - while (1) { - char c = *s++; - if (c == 0) return; - pfun(c); - } -} - -void pint (int i, pfun_t pfun) { - uint32_t j = i; - if (i<0) { pfun('-'); j=-i; } - pintbase(j, 10, pfun); -} - -void pintbase (uint32_t i, uint8_t base, pfun_t pfun) { - int lead = 0; uint32_t p = 1000000000; - if (base == 2) p = 0x80000000; else if (base == 16) p = 0x10000000; - for (uint32_t d=p; d>0; d=d/base) { - uint32_t j = i/d; - if (j!=0 || lead || d==1) { pfun((j<10) ? j+'0' : j+'W'); lead=1;} - i = i - j*d; - } -} - -void pmantissa (float f, pfun_t pfun) { - int sig = floor(log10(f)); - int mul = pow(10, 5 - sig); - int i = round(f * mul); - bool point = false; - if (i == 1000000) { i = 100000; sig++; } - if (sig < 0) { - pfun('0'); pfun('.'); point = true; - for (int j=0; j < - sig - 1; j++) pfun('0'); - } - mul = 100000; - for (int j=0; j<7; j++) { - int d = (int)(i / mul); - pfun(d + '0'); - i = i - d * mul; - if (i == 0) { - if (!point) { - for (int k=j; k= 0) { pfun('.'); point = true; } - mul = mul / 10; - } -} - -void pfloat (float f, pfun_t pfun) { - if (isnan(f)) { pfstring("NaN", pfun); return; } - if (f == 0.0) { pfun('0'); return; } - if (isinf(f)) { pfstring("Inf", pfun); return; } - if (f < 0) { pfun('-'); f = -f; } - // Calculate exponent - int e = 0; - if (f < 1e-3 || f >= 1e5) { - e = floor(log(f) / 2.302585); // log10 gives wrong result - f = f / pow(10, e); - } - - pmantissa (f, pfun); - - // Exponent - if (e != 0) { - pfun('e'); - pint(e, pfun); - } -} - -inline void pln (pfun_t pfun) { - pfun('\n'); -} - -void pfl (pfun_t pfun) { - if (LastPrint != '\n') pfun('\n'); -} - -void plist (object *form, pfun_t pfun) { - pfun('('); - printobject(car(form), pfun); - form = cdr(form); - while (form != NULL && listp(form)) { - pfun(' '); - printobject(car(form), pfun); - form = cdr(form); - } - if (form != NULL) { - pfstring(" . ", pfun); - printobject(form, pfun); - } - pfun(')'); -} - -void pstream (object *form, pfun_t pfun) { - pfun('<'); - pfstring(streamname[(form->integer)>>8], pfun); - pfstring("-stream ", pfun); - pint(form->integer & 0xFF, pfun); - pfun('>'); -} - -void printobject (object *form, pfun_t pfun) { - if (form == NULL) pfstring("nil", pfun); - else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring("", pfun); - else if (listp(form)) plist(form, pfun); - else if (integerp(form)) pint(form->integer, pfun); - else if (floatp(form)) pfloat(form->single_float, pfun); - else if (symbolp(form)) { if (form->name != sym(NOTHING)) printsymbol(form, pfun); } - else if (characterp(form)) pcharacter(form->chars, pfun); - else if (stringp(form)) printstring(form, pfun); - else if (arrayp(form)) printarray(form, pfun); - else if (streamp(form)) pstream(form, pfun); - else error2("error in print"); -} - -void prin1object (object *form, pfun_t pfun) { - char temp = Flags; - clrflag(PRINTREADABLY); - printobject(form, pfun); - Flags = temp; -} - -// Read functions - -int glibrary () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } - char c = LispLibrary[GlobalStringIndex++]; - return (c != 0) ? c : -1; // -1? -} - -void loadfromlibrary (object *env) { - GlobalStringIndex = 0; - object *line = read(glibrary); - while (line != NULL) { - protect(line); - eval(line, env); - unprotect(); - line = read(glibrary); - } -} - -// For line editor -const int TerminalWidth = 80; -volatile int WritePtr = 0, ReadPtr = 0, LastWritePtr = 0; -const int KybdBufSize = 333; // 42*8 - 3 -char KybdBuf[KybdBufSize]; -volatile uint8_t KybdAvailable = 0; - -// Parenthesis highlighting -void esc (int p, char c) { - Serial.write('\e'); Serial.write('['); - Serial.write((char)('0'+ p/100)); - Serial.write((char)('0'+ (p/10) % 10)); - Serial.write((char)('0'+ p % 10)); - Serial.write(c); -} - -void hilight (char c) { - Serial.write('\e'); Serial.write('['); Serial.write(c); Serial.write('m'); -} - -void Highlight (int p, int wp, uint8_t invert) { - wp = wp + 2; // Prompt -#if defined (printfreespace) - int f = Freespace; - while (f) { wp++; f=f/10; } -#endif - int line = wp/TerminalWidth; - int col = wp%TerminalWidth; - int targetline = (wp - p)/TerminalWidth; - int targetcol = (wp - p)%TerminalWidth; - int up = line-targetline, left = col-targetcol; - if (p) { - if (up) esc(up, 'A'); - if (col > targetcol) esc(left, 'D'); else esc(-left, 'C'); - if (invert) hilight('7'); - Serial.write('('); Serial.write('\b'); - // Go back - if (up) esc(up, 'B'); // Down - if (col > targetcol) esc(left, 'C'); else esc(-left, 'D'); - Serial.write('\b'); Serial.write(')'); - if (invert) hilight('0'); - } -} - -void processkey (char c) { - if (c == 27) { setflag(ESCAPE); return; } // Escape key -#if defined(vt100) - static int parenthesis = 0, wp = 0; - // Undo previous parenthesis highlight - Highlight(parenthesis, wp, 0); - parenthesis = 0; -#endif - // Edit buffer - if (c == '\n' || c == '\r') { - pserial('\n'); - KybdAvailable = 1; - ReadPtr = 0; LastWritePtr = WritePtr; - return; - } - if (c == 8 || c == 0x7f) { // Backspace key - if (WritePtr > 0) { - WritePtr--; - Serial.write(8); Serial.write(' '); Serial.write(8); - if (WritePtr) c = KybdBuf[WritePtr-1]; - } - } else if (c == 9) { // tab or ctrl-I - for (int i = 0; i < LastWritePtr; i++) Serial.write(KybdBuf[i]); - WritePtr = LastWritePtr; - } else if (WritePtr < KybdBufSize) { - KybdBuf[WritePtr++] = c; - Serial.write(c); - } -#if defined(vt100) - // Do new parenthesis highlight - if (c == ')') { - int search = WritePtr-1, level = 0; - while (search >= 0 && parenthesis == 0) { - c = KybdBuf[search--]; - if (c == ')') level++; - if (c == '(') { - level--; - if (level == 0) {parenthesis = WritePtr-search-1; wp = WritePtr; } - } - } - Highlight(parenthesis, wp, 1); - } -#endif - return; -} - -int gserial () { - if (LastChar) { - char temp = LastChar; - LastChar = 0; - return temp; - } -#if defined(lineeditor) - while (!KybdAvailable) { - while (!Serial.available()); - char temp = Serial.read(); - processkey(temp); - } - if (ReadPtr != WritePtr) return KybdBuf[ReadPtr++]; - KybdAvailable = 0; - WritePtr = 0; - return '\n'; -#else - unsigned long start = millis(); - while (!Serial.available()) { delay(1); if (millis() - start > 1000) clrflag(NOECHO); } - char temp = Serial.read(); - if (temp != '\n' && !tstflag(NOECHO)) pserial(temp); - return temp; -#endif -} - -object *nextitem (gfun_t gfun) { - int ch = gfun(); - while(issp(ch)) ch = gfun(); - - if (ch == ';') { - do { ch = gfun(); if (ch == ';' || ch == '(') setflag(NOECHO); } - while(ch != '('); - } - if (ch == '\n') ch = gfun(); - if (ch == -1) return nil; - if (ch == ')') return (object *)KET; - if (ch == '(') return (object *)BRA; - if (ch == '\'') return (object *)QUO; - - // Parse string - if (ch == '"') return readstring('"', true, gfun); - - // Parse symbol, character, or number - int index = 0, base = 10, sign = 1; - char buffer[BUFFERSIZE]; - int bufmax = BUFFERSIZE-3; // Max index - unsigned int result = 0; - bool isfloat = false; - float fresult = 0.0; - - if (ch == '+') { - buffer[index++] = ch; - ch = gfun(); - } else if (ch == '-') { - sign = -1; - buffer[index++] = ch; - ch = gfun(); - } else if (ch == '.') { - buffer[index++] = ch; - ch = gfun(); - if (ch == ' ') return (object *)DOT; - isfloat = true; - } - - // Parse reader macros - else if (ch == '#') { - ch = gfun(); - char ch2 = ch & ~0x20; // force to upper case - if (ch == '\\') { // Character - base = 0; ch = gfun(); - if (issp(ch) || isbr(ch)) return character(ch); - else LastChar = ch; - } else if (ch == '|') { - do { while (gfun() != '|'); } - while (gfun() != '#'); - return nextitem(gfun); - } else if (ch2 == 'B') base = 2; - else if (ch2 == 'O') base = 8; - else if (ch2 == 'X') base = 16; - else if (ch == '\'') return nextitem(gfun); - else if (ch == '.') { - setflag(NOESC); - object *result = eval(read(gfun), NULL); - clrflag(NOESC); - return result; - } - else if (ch == '(') { LastChar = ch; return readarray(1, read(gfun)); } - else if (ch == '*') return readbitarray(gfun); - else if (ch >= '1' && ch <= '9' && (gfun() & ~0x20) == 'A') return readarray(ch - '0', read(gfun)); - else error2("illegal character after #"); - ch = gfun(); - } - int valid; // 0=undecided, -1=invalid, +1=valid - if (ch == '.') valid = 0; else if (digitvalue(ch) ((unsigned int)INT_MAX+(1-sign)/2)) - return makefloat((float)result*sign); - return number(result*sign); - } else if (base == 0) { - if (index == 1) return character(buffer[0]); - const char *p = ControlCodes; char c = 0; - while (c < 33) { - if (strcasecmp(buffer, p) == 0) return character(c); - p = p + strlen(p) + 1; c++; - } - if (index == 3) return character((buffer[0]*10+buffer[1])*10+buffer[2]-5328); - error2("unknown character"); - } - - builtin_t x = lookupbuiltin(buffer); - if (x == NIL) return nil; - if (x != ENDFUNCTIONS) return bsymbol(x); - if (index <= 6 && valid40(buffer)) return intern(twist(pack40(buffer))); - return internlong(buffer); -} - -object *readrest (gfun_t gfun) { - object *item = nextitem(gfun); - object *head = NULL; - object *tail = NULL; - - while (item != (object *)KET) { - if (item == (object *)BRA) { - item = readrest(gfun); - } else if (item == (object *)QUO) { - item = cons(bsymbol(QUOTE), cons(read(gfun), NULL)); - } else if (item == (object *)DOT) { - tail->cdr = read(gfun); - if (readrest(gfun) != NULL) error2("malformed list"); - return head; - } else { - object *cell = cons(item, NULL); - if (head == NULL) head = cell; - else tail->cdr = cell; - tail = cell; - item = nextitem(gfun); - } - } - return head; -} - -object *read (gfun_t gfun) { - object *item = nextitem(gfun); - if (item == (object *)KET) error2("incomplete list"); - if (item == (object *)BRA) return readrest(gfun); - if (item == (object *)DOT) return read(gfun); - if (item == (object *)QUO) return cons(bsymbol(QUOTE), cons(read(gfun), NULL)); - return item; -} - -// Setup - -void initenv () { - GlobalEnv = NULL; - tee = bsymbol(TEE); -} - -void initgfx () { - #if defined(gfxsupport) - tft.init(135, 240); - #if defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) - pinMode(TFT_I2C_POWER, OUTPUT); - digitalWrite(TFT_I2C_POWER, HIGH); - tft.setRotation(3); - #else - tft.setRotation(1); - #endif - tft.fillScreen(ST77XX_BLACK); - pinMode(TFT_BACKLITE, OUTPUT); - digitalWrite(TFT_BACKLITE, HIGH); - #endif -} - -// Entry point from the Arduino IDE -void setup () { - Serial.begin(9600); - int start = millis(); - while ((millis() - start) < 5000) { if (Serial) break; } - initworkspace(); - initenv(); - initsleep(); - initgfx(); - pfstring(PSTR("uLisp 4.6 "), pserial); pln(pserial); -} - -// Read/Evaluate/Print loop - -void repl (object *env) { - for (;;) { - randomSeed(micros()); - gc(NULL, env); - #if defined(printfreespace) - pint(Freespace, pserial); - #endif - if (BreakLevel) { - pfstring(" : ", pserial); - pint(BreakLevel, pserial); - } - pserial('>'); pserial(' '); - Context = NIL; - object *line = read(gserial); - if (BreakLevel && line == nil) { pln(pserial); return; } - if (line == (object *)KET) error2("unmatched right bracket"); - protect(line); - pfl(pserial); - line = eval(line, env); - pfl(pserial); - printobject(line, pserial); - unprotect(); - pfl(pserial); - pln(pserial); - } -} - -void loop () { - if (!setjmp(toplevel_handler)) { - #if defined(resetautorun) - volatile int autorun = 12; // Fudge to keep code size the same - #else - volatile int autorun = 13; - #endif - if (autorun == 12) autorunimage(); - } - ulisperror(); - repl(NULL); -} - -void ulisperror () { - // Come here after error - delay(100); while (Serial.available()) Serial.read(); - clrflag(NOESC); BreakLevel = 0; - for (int i=0; i 0) error2(PSTR("wrong number of arguments")); - - // Return time - unsigned long secs = Offset + now; - object *seconds = number(secs%60); - object *minutes = number((secs/60)%60); - object *hours = number((secs/3600)%24); - return cons(hours, cons(minutes, cons(seconds, NULL))); -} - -// Symbol names -const char stringnow[] PROGMEM = "now"; - -// Documentation strings -const char docnow[] PROGMEM = "(now [hh mm ss])\n" -"Sets the current time, or with no arguments returns the current time\n" -"as a list of three integers (hh mm ss)."; - -// Symbol lookup table -const tbl_entry_t lookup_table2[] PROGMEM = { - { stringnow, fn_now, 0203, docnow }, -}; - -// Table cross-reference functions - -tbl_entry_t *tables[] = {lookup_table, lookup_table2}; -const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) }; - -const tbl_entry_t *table (int n) { - return tables[n]; -} - -unsigned int tablesize (int n) { - return tablesizes[n]; -} diff --git a/ulisp.hpp b/ulisp.hpp new file mode 100644 index 0000000..7aab819 --- /dev/null +++ b/ulisp.hpp @@ -0,0 +1,8457 @@ +/* uLisp ESP Release 4.6 - www.ulisp.com + David Johnson-Davies - www.technoblogy.com - 13th June 2024 + + Licensed under the MIT license: https://opensource.org/licenses/MIT +*/ + +#ifndef ULISP_HPP +#define ULISP_HPP + +// Includes + +// #include "LispLibrary.h" +#include +#include +#include +#include +#include +#include +#include + +// Lisp Library +#ifndef LispLibrary +const char LispLibrary[] = ""; +#endif + +#if defined(gfxsupport) +#define COLOR_WHITE ST77XX_WHITE +#define COLOR_BLACK ST77XX_BLACK +#include // Core graphics library +#include // Hardware-specific library for ST7789 +#if defined(ARDUINO_ESP32_DEV) +Adafruit_ST7789 tft = Adafruit_ST7789(5, 16, 19, 18); +#define TFT_BACKLITE 4 +#else +Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, MOSI, SCK, TFT_RST); +#endif +#endif + +#include +#define SDSIZE 172 + +// Platform specific settings + +#define WORDALIGNED __attribute__((aligned(4))) +#define BUFFERSIZE 260 + +#define WORKSPACESIZE (9216 - SDSIZE) /* Cells (8*bytes) */ +#define LITTLEFS +#include "FS.h" +#include + +#ifndef LED_BUILTIN +#define LED_BUILTIN 13 +#endif + +#define MAX_STACK 4000 + + +// C Macros + +#define nil NULL +#define car(x) (((object*)(x))->car) +#define cdr(x) (((object*)(x))->cdr) + +#define first(x) car(x) +#define rest(x) cdr(x) +#define second(x) first(rest(x)) +#define cddr(x) cdr(cdr(x)) +#define third(x) first(cddr(x)) + +#define push(x, y) ((y) = cons((x), (y))) +#define pop(y) ((y) = cdr(y)) + +#define protect(y) push((y), GCStack) +#define unprotect() pop(GCStack) + +#define integerp(x) ((x) != NULL && (x)->type == NUMBER) +#define floatp(x) ((x) != NULL && (x)->type == FLOAT) +#define symbolp(x) ((x) != NULL && (x)->type == SYMBOL) +#define bfunctionp(x) ((x) != NULL && (x)->type == BFUNCTION) +#define stringp(x) ((x) != NULL && (x)->type == STRING) +#define characterp(x) ((x) != NULL && (x)->type == CHARACTER) +#define arrayp(x) ((x) != NULL && (x)->type == ARRAY) +#define streamp(x) ((x) != NULL && (x)->type == STREAM) + +#define mark(x) (car(x) = (object*)(((uintptr_t)(car(x))) | MARKBIT)) +#define unmark(x) (car(x) = (object*)(((uintptr_t)(car(x))) & ~MARKBIT)) +#define marked(x) ((((uintptr_t)(car(x))) & MARKBIT) != 0) +#define MARKBIT 1 + +#define setflag(x) (Flags |= 1 << (x)) +#define clrflag(x) (Flags &= ~(1 << (x))) +#define tstflag(x) (Flags & 1 << (x)) + +#define issp(x) (x == ' ' || x == '\n' || x == '\r' || x == '\t') +#define isbr(x) (x == ')' || x == '(' || x == '"' || x == '#' || x == '\'') +#define longsymbolp(x) longnamep((x)->name) +#define longnamep(x) (((x)&0x03) == 0) +#define arraysize(x) (sizeof(x) / sizeof(x[0])) +#define stringifyX(x) #x +#define stringify(x) stringifyX(x) +#define PACKEDS 0x43238000 +#define BUILTINS 0xF4240000 +#define ENDFUNCTIONS 0x0BDC0000 + +#define fntype(x) (((uint8_t)(x)) >> 6) +#define getminargs(x) ((((uint8_t)(x)) >> 3) & 7) +#define getmaxargs(x) (((uint8_t)(x)) & 7) +#define unlimitedp(x) (getmaxargs(x) == UNLIMITED) +#define UNLIMITED 7 + +// let's hope the compiler can do constant folding!! +#define MINMAX(fntype, min, max) (((fntype) << 6) | ((min) << 3) | (max)) + +// Constants + +#define TRACEMAX 3 // Number of traced functions +enum type { + ZZERO = 0, + SYMBOL = 2, + CODE = 4, + NUMBER = 6, + BFUNCTION = 8, + STREAM = 10, + CHARACTER = 12, + FLOAT = 14, + ARRAY = 16, + STRING = 18, + PAIR = 20 +}; // ARRAY, STRING, and PAIR must be last +enum token { + UNUSED, + OPEN_PAREN, + CLOSE_PAREN, + SINGLE_QUOTE, + PERIOD, + BACKTICK, + COMMA, + COMMA_AT +}; +enum fntypes_t { + OTHER_FORMS, + SPECIAL_FORMS, + FUNCTIONS, + SPECIAL_SYMBOLS +}; + +// Stream names used by printobject +const char serialstream[] = "serial"; +const char i2cstream[] = "i2c"; +const char spistream[] = "spi"; +const char sdstream[] = "sd"; +const char wifistream[] = "wifi"; +const char stringstream[] = "string"; +const char gfxstream[] = "gfx"; +const char* const streamname[] = { + serialstream, i2cstream, spistream, sdstream, wifistream, stringstream, gfxstream +}; +enum stream { + SERIALSTREAM, + I2CSTREAM, + SPISTREAM, + SDSTREAM, + WIFISTREAM, + STRINGSTREAM, + GFXSTREAM +}; + +// Typedefs + +typedef uint32_t symbol_t; +typedef uint8_t minmax_t; +typedef uint32_t builtin_t; +typedef uint16_t flags_t; +typedef uint32_t chars_t; + +typedef struct sobject { + union { + struct { + sobject* car; + sobject* cdr; + }; + struct { + unsigned int type; + union { + symbol_t name; + int integer; + chars_t chars; + float single_float; + }; + }; + }; +} object; + +typedef object* (*fn_ptr_type)(object*, object*); +typedef void (*mapfun_t)(object*, object**); + +typedef const struct { + const char* string; + fn_ptr_type fptr; + minmax_t minmax; + const char* doc; +} tbl_entry_t; + +typedef struct { + tbl_entry_t* table; + size_t size; +} mtbl_entry_t; + +typedef int (*gfun_t)(); +typedef void (*pfun_t)(char); + +enum builtins : builtin_t { + NIL, + TEE, + NOTHING, + OPTIONAL, + FEATURES, + INITIALELEMENT, + ELEMENTTYPE, + TEST, + EQ, + BIT, + AMPREST, + LAMBDA, + MACRO, + LET, + LETSTAR, + CLOSURE, + PSTAR, + QUOTE, + BACKQUOTE, + UNQUOTE, + UNQUOTE_SPLICING, + CONS, + APPEND, + DEFUN, + SETF, + CHAR, + DEFVAR, + DEFMACRO, + CAR, + FIRST, + CDR, + REST, + NTH, + AREF, + STRINGFN, + PINMODE, + DIGITALWRITE, + ANALOGREAD, + REGISTER, + FORMAT +}; + +// Global variables + +object Workspace[WORKSPACESIZE] WORDALIGNED; +mtbl_entry_t* Metatable; +size_t NumTables; + +jmp_buf toplevel_handler; +jmp_buf* handler = &toplevel_handler; +size_t Freespace = 0; +object* Freelist; +builtin_t Context; + +object* tee; +object* GlobalEnv; +object* GCStack = NULL; +object* GlobalString; +object* GlobalStringTail; +object* Thrown; +int GlobalStringIndex = 0; +uint8_t PrintCount = 0; +uint8_t BreakLevel = 0; +char LastChar = 0; +char LastPrint = 0; + +unsigned int I2Ccount; +unsigned int TraceFn[TRACEMAX]; +unsigned int TraceDepth[TRACEMAX]; + +void* StackBottom; + +// Flags +enum flag { + PRINTREADABLY, + RETURNFLAG, + ESCAPE, + EXITEDITOR, + LIBRARYLOADED, + NOESC, + NOECHO, + MUFFLEERRORS, + TAILCALL, + INCATCH +}; +volatile flags_t Flags = 1; // PRINTREADABLY set by default + +// Forward references +bool builtin_keywordp(object*); +inline bool builtinp(symbol_t name); +bool keywordp(object*); +void pfstring(const char*, pfun_t); +char nthchar(object*, int); +void pfl(pfun_t); +void pln(pfun_t); +void pserial(char); +int gserial(); +int glibrary(); +void pstr(char); +void psymbol(symbol_t, pfun_t); +void printobject(object*, pfun_t); +symbol_t sym(builtin_t); +void indent(uint8_t, char, pfun_t); +object* lispstring(const char*); +uint32_t pack40(const char*); +bool valid40(const char*); +char* cstring(object*, char*, int); +void pint(int, pfun_t); +void pintbase(uint32_t, uint8_t, pfun_t); +void printstring(object*, pfun_t); +int subwidthlist(object*, int); +minmax_t getminmax(builtin_t); +fn_ptr_type lookupfn(builtin_t); +int listlength(object*); +void checkminmax(builtin_t, int); +object* findpair(object*, object*); +object* findvalue(object*, object*); +const char* lookupdoc(builtin_t); +void printsymbol(object*, pfun_t); +bool findsubstring(char*, builtin_t); +int stringcompare(object*, bool, bool, bool); +void pbuiltin(builtin_t, pfun_t); +object* value(symbol_t, object*); +void supersub(object*, int, int, pfun_t); +object* sp_progn(object*, object*); +object* progn_no_tc(object*, object*); +object* fn_princtostring(object*, object*); +object* read(gfun_t); +object* eval(object*, object*); +void repl(object*); +void prin1object(object*, pfun_t); +void plispstr(symbol_t, pfun_t); +void testescape(); +bool is_macro_call(object*, object*); + +inline symbol_t twist(builtin_t x) { + return (x << 2) | ((x & 0xC0000000) >> 30); +} + +inline builtin_t untwist(symbol_t x) { + return (x >> 2 & 0x3FFFFFFF) | ((x & 0x03) << 30); +} + +// Error handling + +/* + errorsub - used by all the error routines. + Prints: "Error in fname: string", where fname is the name of the Lisp function in which the error occurred. +*/ +void errorsub(symbol_t fname, const char* string) { + pfl(pserial); + pfstring("Error", pserial); + if (fname != sym(NIL)) { + pfstring(" in ", pserial); + psymbol(fname, pserial); + } + pserial(':'); + pserial(' '); + pfstring(string, pserial); +} + +#ifdef __cplusplus +[[noreturn]] +#endif +void +errorend() { + GCStack = NULL; + longjmp(*handler, 1); +} + +/* + errorsym - prints an error message and reenters the REPL. + Prints: "Error in fname: string: symbol", where fname is the name of the Lisp function in which the error occurred, + and symbol is the object generating the error. +*/ +#ifdef __cplusplus +[[noreturn]] +#endif +void +errorsym(symbol_t fname, const char* string, object* symbol) { + if (!tstflag(MUFFLEERRORS)) { + errorsub(fname, string); + pserial(':'); + pserial(' '); + printobject(symbol, pserial); + pln(pserial); + } + errorend(); +} + +/* + errorsym2 - prints an error message and reenters the REPL. + Prints: "Error in fname: string", where fname is the name of the user Lisp function in which the error occurred. +*/ +#ifdef __cplusplus +[[noreturn]] +#endif +void +errorsym2(symbol_t fname, const char* string) { + if (!tstflag(MUFFLEERRORS)) { + errorsub(fname, string); + pln(pserial); + } + errorend(); +} + +/* + error - prints an error message and reenters the REPL. + Prints: "Error in Context: string: symbol", where Context is the name of the built-in Lisp function in which the error occurred, + and symbol is the object generating the error. +*/ +#ifdef __cplusplus +[[noreturn]] +#endif +void +error(const char* string, object* symbol) { + errorsym(sym(Context), string, symbol); +} + +/* + error2 - prints an error message and reenters the REPL. + Prints: "Error in Context: string", where Context is the name of the built-in Lisp function in which the error occurred. +*/ +#ifdef __cplusplus +[[noreturn]] +#endif +void +error2(const char* string) { + errorsym2(sym(Context), string); +} + +/* + formaterr - displays a format error with a ^ pointing to the error +*/ +#ifdef __cplusplus +[[noreturn]] +#endif +void +formaterr(object* formatstr, const char* string, uint8_t p) { + pln(pserial); + indent(4, ' ', pserial); + printstring(formatstr, pserial); + pln(pserial); + indent(p + 5, ' ', pserial); + pserial('^'); + error2(string); + pln(pserial); + GCStack = NULL; + longjmp(*handler, 1); +} + +// Save space as these are used multiple times +const char notanumber[] = "argument is not a number"; +const char notaninteger[] = "argument is not an integer"; +const char notastring[] = "argument is not a string"; +const char notalist[] = "argument is not a list"; +const char notasymbol[] = "argument is not a symbol"; +const char notproper[] = "argument is not a proper list"; +const char toomanyargs[] = "too many arguments"; +const char toofewargs[] = "too few arguments"; +const char noargument[] = "missing argument"; +const char nostream[] = "missing stream argument"; +const char overflow[] = "arithmetic overflow"; +const char divisionbyzero[] = "division by zero"; +const char indexnegative[] = "index can't be negative"; +const char invalidarg[] = "invalid argument"; +const char invalidkey[] = "invalid keyword"; +const char illegalclause[] = "illegal clause"; +const char invalidpin[] = "invalid pin"; +const char oddargs[] = "odd number of arguments"; +const char indexrange[] = "index out of range"; +const char canttakecar[] = "can't take car"; +const char canttakecdr[] = "can't take cdr"; +const char unknownstreamtype[] = "unknown stream type"; + +// Set up workspace + +/* + initworkspace - initialises the workspace into a linked list of free objects +*/ +void initworkspace() { + Freelist = NULL; + for (int i = WORKSPACESIZE - 1; i >= 0; i--) { + object* obj = &Workspace[i]; + car(obj) = NULL; + cdr(obj) = Freelist; + Freelist = obj; + Freespace++; + } +} + +/* + myalloc - returns the first object from the linked list of free objects +*/ +object* myalloc() { + if (Freespace == 0) { + Context = NIL; + error2("out of memory"); + } + object* temp = Freelist; + Freelist = cdr(Freelist); + Freespace--; + return temp; +} + +/* + myfree - adds obj to the linked list of free objects. + inline makes gc significantly faster +*/ +inline void myfree(object* obj) { + car(obj) = NULL; + cdr(obj) = Freelist; + Freelist = obj; + Freespace++; +} + +// Make each type of object + +/* + number - make an integer object with value n and return it + or return the existing one with the same value +*/ +object* number(int n) { + for (int i = 0; i < WORKSPACESIZE; i++) { + object* obj = &Workspace[i]; + if (obj->type == NUMBER && obj->integer == n) return obj; + } + object* ptr = myalloc(); + ptr->type = NUMBER; + ptr->integer = n; + return ptr; +} + +/* + makefloat - make a floating point object with value f and return it + or return the existing one with the same value +*/ +object* makefloat(float f) { + for (int i = 0; i < WORKSPACESIZE; i++) { + object* obj = &Workspace[i]; + if (obj->type == FLOAT && obj->single_float == f) return obj; + } + object* ptr = myalloc(); + ptr->type = FLOAT; + ptr->single_float = f; + return ptr; +} + +/* + character - make a character object with value c and return it + or return the existing one with the same value +*/ +object* character(char c) { + for (int i = 0; i < WORKSPACESIZE; i++) { + object* obj = &Workspace[i]; + if (obj->type == CHARACTER && obj->chars == c) return obj; + } + object* ptr = myalloc(); + ptr->type = CHARACTER; + ptr->chars = c; + return ptr; +} + +/* + cons - make a cons with arg1 and arg2 return it +*/ +object* cons(object* arg1, object* arg2) { + object* ptr = myalloc(); + ptr->car = arg1; + ptr->cdr = arg2; + return ptr; +} + +/* + symbol - make a symbol object with value name and return it + or returns the existing one with the same value +*/ +object* symbol(symbol_t name) { + for (int i = 0; i < WORKSPACESIZE; i++) { + object* obj = &Workspace[i]; + if (obj->type == SYMBOL && obj->name == name) return obj; + } + object* ptr = myalloc(); + ptr->type = SYMBOL; + ptr->name = name; + return ptr; +} + +object* bfunction_from_symbol(object* symbol) { + if (!(symbolp(symbol) && builtinp(symbol->name))) return nil; + symbol_t nm = symbol->name; + for (int i = 0; i < WORKSPACESIZE; i++) { + object* obj = &Workspace[i]; + if (obj->type == BFUNCTION && obj->name == nm) return obj; + } + object* ptr = myalloc(); + ptr->type = BFUNCTION; + ptr->name = nm; + return ptr; +} + +/* + bsymbol - make a built-in symbol +*/ +inline object* bsymbol(builtin_t name) { + return symbol(twist(name + BUILTINS)); +} + +/* + eqsymbols - compares the long string/symbol obj with the string in buffer. +*/ +bool eqsymbols(object* obj, const char* buffer) { + object* arg = cdr(obj); + int i = 0; + while (!(arg == NULL && buffer[i] == 0)) { + if (arg == NULL || buffer[i] == 0) return false; + int test = 0, shift = 24; + for (int j = 0; j < 4; j++, i++) { + if (buffer[i] == 0) break; + test |= buffer[i] << shift; + shift -= 8; + } + if (arg->chars != test) return false; + arg = car(arg); + } + return true; +} + +/* + internlong - looks through the workspace for an existing occurrence of the long symbol in buffer and returns it, + otherwise calls lispstring(buffer) and coerces it to symbol. +*/ +object* internlong(const char* buffer) { + for (int i = 0; i < WORKSPACESIZE; i++) { + object* obj = &Workspace[i]; + if (obj->type == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) return obj; + } + object* obj = lispstring(buffer); + obj->type = SYMBOL; + return obj; +} + +/* + buftosymbol - checks the characters in buffer and calls symbol() or internlong() to make it a symbol. +*/ +object* buftosymbol(const char* b) { + int l = strlen(b); + if (l <= 6 && valid40(b)) return symbol(twist(pack40(b))); + else return internlong(b); +} + +/* + stream - makes a stream object defined by streamtype and address, and returns it +*/ +object* stream(uint8_t streamtype, uint8_t address) { + object* ptr = myalloc(); + ptr->type = STREAM; + ptr->integer = streamtype << 8 | address; + return ptr; +} + +/* + newstring - makes an empty string object and returns it +*/ +object* newstring() { + object* ptr = myalloc(); + ptr->type = STRING; + ptr->chars = 0; + return ptr; +} + +// Features + +const char floatingpoint[] = ":floating-point"; +const char arrays[] = ":arrays"; +const char doc[] = ":documentation"; +const char errorhandling[] = ":error-handling"; +const char wifi[] = ":wi-fi"; +const char gfx[] = ":gfx"; + +/* + *features* - create a list of features symbols from const strings. +*/ +object* ss_features(object* args, object* env) { + (void)env; + if (args) error2("*features* is read only"); + object* result = NULL; +#ifdef gfxsupport + push(internlong(gfx), result); +#endif + push(internlong(wifi), result); + push(internlong(errorhandling), result); + push(internlong(doc), result); + push(internlong(arrays), result); + push(internlong(floatingpoint), result); + return result; +} + +// Garbage collection + +/* + markobject - recursively marks reachable objects, starting from obj +*/ +void markobject(object* obj) { +MARK: + if (obj == NULL) return; + if (marked(obj)) return; + + object* arg = car(obj); + unsigned int type = obj->type; + mark(obj); + + if (type >= PAIR || type == ZZERO) { // cons + markobject(arg); + obj = cdr(obj); + goto MARK; + } + + if (type == ARRAY) { + obj = cdr(obj); + goto MARK; + } + + if ((type == STRING) || (type == SYMBOL && longsymbolp(obj))) { + obj = cdr(obj); + while (obj != NULL) { + arg = car(obj); + mark(obj); + obj = arg; + } + } +} + +/* + sweep - goes through the workspace freeing objects that have not been marked, + and unmarks marked objects +*/ +void sweep() { + Freelist = NULL; + Freespace = 0; + for (int i = WORKSPACESIZE - 1; i >= 0; i--) { + object* obj = &Workspace[i]; + if (marked(obj)) unmark(obj); + else myfree(obj); + } +} + +/* + gc - performs garbage collection by calling markobject() on each of the pointers to objects in use, + followed by sweep() to free unused objects. +*/ +void gc(object* form, object* env) { +#if defined(printgcs) + int start = Freespace; + static int GC_Count = 0; +#endif + markobject(tee); + markobject(Thrown); + markobject(GlobalEnv); + markobject(GCStack); + markobject(form); + markobject(env); + sweep(); +#if defined(printgcs) + GC_Count++; + pfl(pserial); + pfstring("{GC#", pserial); + pint(GC_Count, pserial); + pserial(':'); + pint(Freespace - start, pserial); + pserial(','); + pint(Freespace, pserial); + pserial('/'); + pint(WORKSPACESIZE, pserial); + pserial('}'); +#endif +} + +char* MakeFilename(object* arg, char* buffer) { + int max = BUFFERSIZE - 1; + buffer[0] = '/'; + int i = 1; + do { + char c = nthchar(arg, i - 1); + if (c == '\0') break; + buffer[i++] = c; + } while (i < max); + buffer[i] = '\0'; + return buffer; +} + +// Tracing + +/* + tracing - returns a number between 1 and TRACEMAX if name is being traced, or 0 otherwise +*/ +int tracing(symbol_t name) { + int i = 0; + while (i < TRACEMAX) { + if (TraceFn[i] == name) return i + 1; + i++; + } + return 0; +} + +/* + trace - enables tracing of symbol name and adds it to the array TraceFn[]. +*/ +void trace(symbol_t name) { + if (tracing(name)) error("already being traced", symbol(name)); + int i = 0; + while (i < TRACEMAX) { + if (TraceFn[i] == 0) { + TraceFn[i] = name; + TraceDepth[i] = 0; + return; + } + i++; + } + error2("already tracing " stringify(TRACEMAX) " functions"); +} + +/* + untrace - disables tracing of symbol name and removes it from the array TraceFn[]. +*/ +void untrace(symbol_t name) { + int i = 0; + while (i < TRACEMAX) { + if (TraceFn[i] == name) { + TraceFn[i] = 0; + return; + } + i++; + } + error("not tracing", symbol(name)); +} + +// Helper functions + +/* + consp - implements Lisp consp +*/ +bool consp(object* x) { + if (x == NULL) return false; + unsigned int type = x->type; + return type >= PAIR || type == ZZERO; +} + +/* + atom - implements Lisp atom +*/ +#define atom(x) (!consp(x)) + +/* + listp - implements Lisp listp +*/ +bool listp(object* x) { + if (x == NULL) return true; + unsigned int type = x->type; + return type >= PAIR || type == ZZERO; +} + +/* + improperp - tests whether x is an improper list +*/ +#define improperp(x) (!listp(x)) + +/* + quoteit - quote a symbol with the specified type of quote +*/ + +object* quoteit(builtin_t q, object* it) { + return cons(bsymbol(q), cons(it, nil)); +} + +// Radix 40 encoding + +/* + builtin - converts a symbol name to builtin +*/ +builtin_t builtin(symbol_t name) { + return (builtin_t)(untwist(name) - BUILTINS); +} + +/* + sym - converts a builtin to a symbol name +*/ +symbol_t sym(builtin_t x) { + return twist(x + BUILTINS); +} + +const char radix40alphabet[] = "\0000123456789abcdefghijklmnopqrstuvwxyz-*$"; + +/* + toradix40 - returns a number from 0 to 39 if the character can be encoded, or -1 otherwise. +*/ +int8_t toradix40(char ch) { + ch = tolower(ch); + for (int8_t i = 0; i < 40; i++) { + if (radix40alphabet[i] == ch) return i; + } + return -1; // Invalid +} + +/* + fromradix40 - returns the character encoded by the number n. +*/ +char fromradix40(char n) { + if (n < 0 || n >= 40) return 0; + return radix40alphabet[n]; +} + +/* + pack40 - packs six radix40-encoded characters from buffer into a 32-bit number and returns it. +*/ +uint32_t pack40(const char* buffer) { + int x = 0, gz = 0, c = 0; + for (int i = 0; i < 6; i++) { + if (gz) c = 0; + else c = buffer[i]; // Don't dereference the buffer if we reached the end of the string already + x *= 40; + if (c == 0) gz = 1; + else x += toradix40(c); + } + return x; +} + +/* + valid40 - returns true if the symbol in buffer can be encoded as six radix40-encoded characters. +*/ +bool valid40(const char* buffer) { + int t = 11; + for (int i = 0; i < 6; i++) { + if (toradix40(buffer[i]) < t) return false; + if (buffer[i] == 0) break; + t = 0; + } + return true; +} + +/* + digitvalue - returns the numerical value of a hexadecimal digit, or 16 if invalid. +*/ +int8_t digitvalue(char d) { + if (d >= '0' && d <= '9') return d - '0'; + d = d | 0x20; + if (d >= 'a' && d <= 'f') return d - 'a' + 10; + return 16; +} + +/* + checkinteger - check that obj is an integer and return it +*/ +int checkinteger(object* obj) { + if (!integerp(obj)) error(notaninteger, obj); + return obj->integer; +} + +/* + checkbitvalue - check that obj is an integer equal to 0 or 1 and return it +*/ +int checkbitvalue(object* obj) { + if (!integerp(obj)) error(notaninteger, obj); + int n = obj->integer; + if (n & ~1) error("argument is not a bit value", obj); + return n; +} + +/* + checkintfloat - check that obj is an integer or floating-point number and return the number +*/ +float checkintfloat(object* obj) { + if (integerp(obj)) return (float)obj->integer; + if (!floatp(obj)) error(notanumber, obj); + return obj->single_float; +} + +/* + checkchar - check that obj is a character and return the character +*/ +int checkchar(object* obj) { + if (!characterp(obj)) error("argument is not a character", obj); + return obj->chars; +} + +/* + checkstring - check that obj is a string +*/ +object* checkstring(object* obj) { + if (!stringp(obj)) error(notastring, obj); + return obj; +} + +int isstream(object* obj) { + if (!streamp(obj)) error("not a stream", obj); + return obj->integer; +} + +int isbuiltin(object* obj, builtin_t n) { + return symbolp(obj) && obj->name == sym(n); +} + +inline bool builtinp(symbol_t name) { + return (untwist(name) >= BUILTINS); +} + +int checkkeyword(object* obj) { + if (!builtin_keywordp(obj)) error("argument is not a keyword", obj); + builtin_t kname = builtin(obj->name); + minmax_t context = getminmax(kname); + if (context != 0 && context != (minmax_t)Context) error(invalidkey, obj); + return ((int)lookupfn(kname)); +} + +/* + checkargs - checks that the number of objects in the list args + is within the range specified in the symbol lookup table +*/ +void checkargs(object* args) { + int nargs = listlength(args); + checkminmax(Context, nargs); +} + +/* + eq - implements Lisp eq +*/ +boolean eq(object* arg1, object* arg2) { + if (arg1 == arg2) return true; // Same object + if ((arg1 == nil) || (arg2 == nil)) return false; // Not both values + if (arg1->cdr != arg2->cdr) return false; // Different values + if (symbolp(arg1) && symbolp(arg2)) return true; // Same symbol + if (integerp(arg1) && integerp(arg2)) return true; // Same integer + if (floatp(arg1) && floatp(arg2)) return true; // Same float + if (characterp(arg1) && characterp(arg2)) return true; // Same character + return false; +} + +/* + equal - implements Lisp equal +*/ +bool equal(object* arg1, object* arg2) { + if (stringp(arg1) && stringp(arg2)) return (stringcompare(cons(arg1, cons(arg2, nil)), false, false, true) != -1); + if (consp(arg1) && consp(arg2)) return (equal(car(arg1), car(arg2)) && equal(cdr(arg1), cdr(arg2))); + return eq(arg1, arg2); +} + +/* + listlength - returns the length of a list +*/ +int listlength(object* list) { + int length = 0; + while (list != NULL) { + if (improperp(list)) error2(notproper); + list = cdr(list); + length++; + } + return length; +} + +/* + checkarguments - checks the arguments list in a special form such as with-xxx, + dolist, or dotimes. +*/ +object* checkarguments(object* args, int min, int max) { + if (args == NULL) error2(noargument); + args = first(args); + if (!listp(args)) error(notalist, args); + int length = listlength(args); + if (length < min) error(toofewargs, args); + if (length > max) error(toomanyargs, args); + return args; +} + +// Mathematical helper functions + +/* + add_floats - used by fn_add + Converts the numbers in args to floats, adds them to fresult, and returns the sum as a Lisp float. +*/ +object* add_floats(object* args, float fresult) { + while (args != NULL) { + object* arg = car(args); + fresult = fresult + checkintfloat(arg); + args = cdr(args); + } + return makefloat(fresult); +} + +/* + subtract_floats - used by fn_subtract with more than one argument + Converts the numbers in args to floats, subtracts them from fresult, and returns the result as a Lisp float. +*/ +object* subtract_floats(object* args, float fresult) { + while (args != NULL) { + object* arg = car(args); + fresult = fresult - checkintfloat(arg); + args = cdr(args); + } + return makefloat(fresult); +} + +/* + negate - used by fn_subtract with one argument + If the result is an integer, and negating it doesn't overflow, keep the result as an integer. + Otherwise convert the result to a float, negate it, and return the result as a Lisp float. +*/ +object* negate(object* arg) { + if (integerp(arg)) { + int result = arg->integer; + if (result == INT_MIN) return makefloat(-result); + else return number(-result); + } else if (floatp(arg)) return makefloat(-(arg->single_float)); + else error(notanumber, arg); + return nil; +} + +/* + multiply_floats - used by fn_multiply + Converts the numbers in args to floats, adds them to fresult, and returns the result as a Lisp float. +*/ +object* multiply_floats(object* args, float fresult) { + while (args != NULL) { + object* arg = car(args); + fresult = fresult * checkintfloat(arg); + args = cdr(args); + } + return makefloat(fresult); +} + +/* + divide_floats - used by fn_divide + Converts the numbers in args to floats, divides fresult by them, and returns the result as a Lisp float. +*/ +object* divide_floats(object* args, float fresult) { + while (args != NULL) { + object* arg = car(args); + float f = checkintfloat(arg); + if (f == 0.0) error2(divisionbyzero); + fresult = fresult / f; + args = cdr(args); + } + return makefloat(fresult); +} + +/* + compare - a generic compare function + Used to implement the other comparison functions. + If lt is true the result is true if each argument is less than the next argument. + If gt is true the result is true if each argument is greater than the next argument. + If eq is true the result is true if each argument is equal to the next argument. +*/ +object* compare(object* args, bool lt, bool gt, bool eq) { + object* arg1 = first(args); + args = cdr(args); + while (args != NULL) { + object* arg2 = first(args); + if (integerp(arg1) && integerp(arg2)) { + if (!lt && ((arg1->integer) < (arg2->integer))) return nil; + if (!eq && ((arg1->integer) == (arg2->integer))) return nil; + if (!gt && ((arg1->integer) > (arg2->integer))) return nil; + } else { + if (!lt && (checkintfloat(arg1) < checkintfloat(arg2))) return nil; + if (!eq && (checkintfloat(arg1) == checkintfloat(arg2))) return nil; + if (!gt && (checkintfloat(arg1) > checkintfloat(arg2))) return nil; + } + arg1 = arg2; + args = cdr(args); + } + return tee; +} + +/* + intpower - calculates base to the power exp as an integer +*/ +int intpower(int base, int exp) { + int result = 1; + while (exp) { + if (exp & 1) result = result * base; + exp = exp / 2; + base = base * base; + } + return result; +} + +// Association lists + +/* + testargument - handles the :test argument for functions that accept it +*/ +object* testargument(object* args) { + object* test = bfunction_from_symbol(bsymbol(EQ)); + if (args != NULL) { + if (cdr(args) == NULL) error("dangling keyword", first(args)); + if (isbuiltin(first(args), TEST)) test = second(args); + else error("unsupported keyword", first(args)); + } + return test; +} + +/* + assoc - looks for key in an association list and returns the matching pair, or nil if not found +*/ +object* assoc(object* key, object* list) { + while (list != NULL) { + if (improperp(list)) error(notproper, list); + object* pair = first(list); + if (!listp(pair)) error("element is not a list", pair); + if (pair != NULL && eq(key, car(pair))) return pair; + list = cdr(list); + } + return nil; +} + +/* + delassoc - deletes the pair matching key from an association list and returns the key, or nil if not found +*/ +object* delassoc(object* key, object** alist) { + object* list = *alist; + object* prev = NULL; + while (list != NULL) { + object* pair = first(list); + if (eq(key, car(pair))) { + if (prev == NULL) *alist = cdr(list); + else cdr(prev) = cdr(list); + return key; + } + prev = list; + list = cdr(list); + } + return nil; +} + +// Array utilities + +/* + nextpower2 - returns the smallest power of 2 that is equal to or greater than n +*/ +int nextpower2(int n) { + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n < 2 ? 2 : n; +} + +/* + buildarray - builds an array with n elements using a tree of size s which must be a power of 2 + The elements are initialised to the default def +*/ +object* buildarray(int n, int s, object* def) { + int s2 = s >> 1; + if (s2 == 1) { + if (n == 2) return cons(def, def); + else if (n == 1) return cons(def, NULL); + else return NULL; + } else if (n >= s2) return cons(buildarray(s2, s2, def), buildarray(n - s2, s2, def)); + else return cons(buildarray(n, s2, def), nil); +} + +object* makearray(object* dims, object* def, bool bitp) { + int size = 1; + object* dimensions = dims; + while (dims != NULL) { + int d = car(dims)->integer; + if (d < 0) error2("dimension can't be negative"); + size = size * d; + dims = cdr(dims); + } + // Bit array identified by making first dimension negative + if (bitp) { + size = (size + sizeof(int) * 8 - 1) / (sizeof(int) * 8); + car(dimensions) = number(-(car(dimensions)->integer)); + } + object* ptr = myalloc(); + ptr->type = ARRAY; + object* tree = nil; + if (size != 0) tree = buildarray(size, nextpower2(size), def); + ptr->cdr = cons(tree, dimensions); + return ptr; +} + +/* + arrayref - returns a pointer to the element specified by index in the array of size s +*/ +object** arrayref(object* array, int index, int size) { + int mask = nextpower2(size) >> 1; + object** p = &car(cdr(array)); + while (mask) { + if ((index & mask) == 0) p = &(car(*p)); + else p = &(cdr(*p)); + mask = mask >> 1; + } + return p; +} + +/* + getarray - gets a pointer to an element in a multi-dimensional array, given a list of the subscripts subs + If the first subscript is negative it's a bit array and bit is set to the bit number +*/ +object** getarray(object* array, object* subs, object* env, int* bit) { + int index = 0, size = 1, s; + *bit = -1; + bool bitp = false; + object* dims = cddr(array); + while (dims != NULL && subs != NULL) { + int d = car(dims)->integer; + if (d < 0) { + d = -d; + bitp = true; + } + if (env) s = checkinteger(eval(car(subs), env)); + else s = checkinteger(car(subs)); + if (s < 0 || s >= d) error("subscript out of range", car(subs)); + size = size * d; + index = index * d + s; + dims = cdr(dims); + subs = cdr(subs); + } + if (dims != NULL) error2("too few subscripts"); + if (subs != NULL) error2("too many subscripts"); + if (bitp) { + size = (size + sizeof(int) * 8 - 1) / (sizeof(int) * 8); + *bit = index & (sizeof(int) == 4 ? 0x1F : 0x0F); + index = index >> (sizeof(int) == 4 ? 5 : 4); + } + return arrayref(array, index, size); +} + +/* + rslice - reads a slice of an array recursively +*/ +void rslice(object* array, int size, int slice, object* dims, object* args) { + int d = first(dims)->integer; + for (int i = 0; i < d; i++) { + int index = slice * d + i; + if (!consp(args)) error2("initial contents don't match array type"); + if (cdr(dims) == NULL) { + object** p = arrayref(array, index, size); + *p = car(args); + } else rslice(array, size, index, cdr(dims), car(args)); + args = cdr(args); + } +} + +/* + readarray - reads a list structure from args and converts it to a d-dimensional array. + Uses rslice for each of the slices of the array. +*/ +object* readarray(int d, object* args) { + object* list = args; + object* dims = NULL; + object* head = NULL; + int size = 1; + for (int i = 0; i < d; i++) { + if (!listp(list)) error2("initial contents don't match array type"); + int l = listlength(list); + if (dims == NULL) { + dims = cons(number(l), NULL); + head = dims; + } else { + cdr(dims) = cons(number(l), NULL); + dims = cdr(dims); + } + size = size * l; + if (list != NULL) list = car(list); + } + object* array = makearray(head, NULL, false); + rslice(array, size, 0, head, args); + return array; +} + +/* + readbitarray - reads an item in the format #*1010101000110 by reading it and returning a list of integers, + and then converting that to a bit array +*/ +object* readbitarray(gfun_t gfun) { + char ch = gfun(); + object* head = NULL; + object* tail = NULL; + while (!issp(ch) && !isbr(ch)) { + if (ch != '0' && ch != '1') error2("illegal character in bit array"); + object* cell = cons(number(ch - '0'), NULL); + if (head == NULL) head = cell; + else tail->cdr = cell; + tail = cell; + ch = gfun(); + } + LastChar = ch; + int size = listlength(head); + object* array = makearray(cons(number(size), NULL), number(0), true); + size = (size + sizeof(int) * 8 - 1) / (sizeof(int) * 8); + int index = 0; + while (head != NULL) { + object** loc = arrayref(array, index >> (sizeof(int) == 4 ? 5 : 4), size); + int bit = index & (sizeof(int) == 4 ? 0x1F : 0x0F); + *loc = number((((*loc)->integer) & ~(1 << bit)) | (car(head)->integer) << bit); + index++; + head = cdr(head); + } + return array; +} + +/* + pslice - prints a slice of an array recursively +*/ +void pslice(object* array, int size, int slice, object* dims, pfun_t pfun, bool bitp) { + bool spaces = true; + if (slice == -1) { + spaces = false; + slice = 0; + } + int d = first(dims)->integer; + if (d < 0) d = -d; + for (int i = 0; i < d; i++) { + if (i && spaces) pfun(' '); + int index = slice * d + i; + if (cdr(dims) == NULL) { + if (bitp) pint(((*arrayref(array, index >> (sizeof(int) == 4 ? 5 : 4), size))->integer) >> (index & (sizeof(int) == 4 ? 0x1F : 0x0F)) & 1, pfun); + else printobject(*arrayref(array, index, size), pfun); + } else { + pfun('('); + pslice(array, size, index, cdr(dims), pfun, bitp); + pfun(')'); + } + } +} + +/* + printarray - prints an array in the appropriate Lisp format +*/ +void printarray(object* array, pfun_t pfun) { + object* dimensions = cddr(array); + object* dims = dimensions; + bool bitp = false; + int size = 1, n = 0; + while (dims != NULL) { + int d = car(dims)->integer; + if (d < 0) { + bitp = true; + d = -d; + } + size = size * d; + dims = cdr(dims); + n++; + } + if (bitp) size = (size + sizeof(int) * 8 - 1) / (sizeof(int) * 8); + pfun('#'); + if (n == 1 && bitp) { + pfun('*'); + pslice(array, size, -1, dimensions, pfun, bitp); + } else { + if (n > 1) { + pint(n, pfun); + pfun('A'); + } + pfun('('); + pslice(array, size, 0, dimensions, pfun, bitp); + pfun(')'); + } +} + +// String utilities + +void indent(uint8_t spaces, char ch, pfun_t pfun) { + for (uint8_t i = 0; i < spaces; i++) pfun(ch); +} + +/* + startstring - starts building a string +*/ +object* startstring() { + object* string = newstring(); + GlobalString = string; + GlobalStringTail = string; + return string; +} + +/* + princtostring - implements Lisp princtostring function +*/ +object* princtostring(object* arg) { + object* obj = startstring(); + prin1object(arg, pstr); + return obj; +} + +/* + buildstring - adds a character on the end of a string + Handles Lisp strings packed four characters per 32-bit word +*/ +void buildstring(char ch, object** tail) { + object* cell; + if (cdr(*tail) == NULL) { + cell = myalloc(); + cdr(*tail) = cell; + } else if (((*tail)->chars & 0xFFFFFF) == 0) { + (*tail)->chars |= ch << 16; + return; + } else if (((*tail)->chars & 0xFFFF) == 0) { + (*tail)->chars |= ch << 8; + return; + } else if (((*tail)->chars & 0xFF) == 0) { + (*tail)->chars |= ch; + return; + } else { + cell = myalloc(); + car(*tail) = cell; + } + car(cell) = NULL; + cell->chars = ch << 24; + *tail = cell; +} + +/* + copystring - returns a copy of a Lisp string +*/ +object* copystring(object* arg) { + object* obj = newstring(); + object* ptr = obj; + arg = cdr(arg); + while (arg != NULL) { + object* cell = myalloc(); + car(cell) = NULL; + if (cdr(obj) == NULL) cdr(obj) = cell; + else car(ptr) = cell; + ptr = cell; + ptr->chars = arg->chars; + arg = car(arg); + } + return obj; +} + +/* + readstring - reads characters from an input stream up to delimiter delim + and returns a Lisp string +*/ +object* readstring(char delim, bool do_escape, gfun_t gfun) { + object* obj = newstring(); + object* tail = obj; + int ch = gfun(); + if (ch == -1) return nil; + while ((ch != delim) && (ch != -1)) { + if (do_escape && ch == '\\') ch = gfun(); + buildstring(ch, &tail); + ch = gfun(); + } + return obj; +} + +/* + stringlength - returns the length of a Lisp string + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word +*/ +int stringlength(object* form) { + int length = 0; + form = cdr(form); + while (form != NULL) { + int chars = form->chars; + for (int i = (sizeof(int) - 1) * 8; i >= 0; i = i - 8) { + if (chars >> i & 0xFF) length++; + } + form = car(form); + } + return length; +} + +/* + getcharplace - gets character n in a Lisp string, and sets shift to (- the shift position -2) + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word. +*/ +object** getcharplace(object* string, int n, int* shift) { + object** arg = &cdr(string); + int top; + if /* constexpr */ (sizeof(int) == 4) { + top = n >> 2; + *shift = 3 - (n & 3); + } else { + top = n >> 1; + *shift = 1 - (n & 1); + } + *shift = -(*shift + 2); + for (int i = 0; i < top; i++) { + if (*arg == NULL) break; + arg = &car(*arg); + } + return arg; +} + +/* + nthchar - returns the nth character from a Lisp string +*/ +char nthchar(object* string, int n) { + int shift; + object** arg = getcharplace(string, n, &shift); + if (*arg == NULL) return '\0'; + return (((*arg)->chars) >> ((-shift - 2) << 3)) & 0xFF; +} + +/* + gstr - reads a character from a string stream +*/ +int gstr() { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + char c = nthchar(GlobalString, GlobalStringIndex++); + if (c != 0) return c; + return '\n'; // -1? +} + +/* + pstr - prints a character to a string stream +*/ +void pstr(char c) { + buildstring(c, &GlobalStringTail); +} + +/* + iptostring - converts a 32-bit IP address to a lisp string +*/ +object* iptostring(uint32_t ip) { + union { + uint32_t data2; + uint8_t u8[4]; + }; + object* obj = startstring(); + data2 = ip; + for (int i = 0; i < 4; i++) { + if (i) pstr('.'); + pintbase(u8[i], 10, pstr); + } + return obj; +} + +/* + lispstring - converts a C string to a Lisp string +*/ +object* lispstring(const char* s) { + object* obj = newstring(); + object* tail = obj; + for (;;) { + char ch = *s++; + if (ch == '\0') break; + if (ch == '\\') ch = *s++; + buildstring(ch, &tail); + } + return obj; +} + +/* + stringcompare - a generic string compare function + Used to implement the other string comparison functions. + Returns -1 if the comparison is false, or the index of the first mismatch if it is true. + If lt is true the result is true if the first argument is less than the second argument. + If gt is true the result is true if the first argument is greater than the second argument. + If eq is true the result is true if the first argument is equal to the second argument. +*/ +int stringcompare(object* args, bool lt, bool gt, bool eq) { + object* arg1 = checkstring(first(args)); + object* arg2 = checkstring(second(args)); + arg1 = cdr(arg1); + arg2 = cdr(arg2); + int m = 0; + chars_t a = 0, b = 0; + while (arg1 || arg2) { + if (!arg1) return lt ? m : -1; + if (!arg2) return gt ? m : -1; + a = arg1->chars; + b = arg2->chars; + if (a < b) { + if (lt) { + m += sizeof(int); + while (a != b) { + m--; + a = a >> 8; + b = b >> 8; + } + return m; + } else return -1; + } + if (a > b) { + if (gt) { + m += sizeof(int); + while (a != b) { + m--; + a = a >> 8; + b = b >> 8; + } + return m; + } else return -1; + } + arg1 = car(arg1); + arg2 = car(arg2); + m += sizeof(int); + } + if (eq) { + m -= sizeof(int); + while (a != 0) { + m++; + a = a << 8; + } + return m; + } + return -1; +} + +/* + documentation - returns the documentation string of a built-in or user-defined function. +*/ +object* documentation(object* arg, object* env) { + if (arg == NULL) return nil; + if (!symbolp(arg)) error(notasymbol, arg); + object* pair = findpair(arg, env); + if (pair != NULL) { + object* val = cdr(pair); + if (listp(val) && first(val)->name == sym(LAMBDA) && cdr(val) != NULL && cddr(val) != NULL) { + if (stringp(third(val))) return third(val); + } + } + symbol_t docname = arg->name; + if (!builtinp(docname)) return nil; + const char* docstring = lookupdoc(builtin(docname)); + if (docstring == NULL) return nil; + object* obj = startstring(); + pfstring(docstring, pstr); + return obj; +} + +/* + apropos - finds the user-defined and built-in functions whose names contain the specified string or symbol, + and prints them if print is true, or returns them in a list. +*/ +object* apropos(object* arg, bool print) { + char buf[17], buf2[33]; + char* part = cstring(princtostring(arg), buf, 17); + object* result = cons(NULL, NULL); + object* ptr = result; + // User-defined? + object* globals = GlobalEnv; + while (globals != NULL) { + object* pair = first(globals); + object* var = car(pair); + object* val = cdr(pair); + char* full = cstring(princtostring(var), buf2, 33); + if (strstr(full, part) != NULL) { + if (print) { + printsymbol(var, pserial); + pserial(' '); + pserial('('); + if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) pfstring("user function", pserial); + else if (consp(val) && car(val)->type == CODE) pfstring("code", pserial); + else pfstring("user symbol", pserial); + pserial(')'); + pln(pserial); + } else { + cdr(ptr) = cons(var, NULL); + ptr = cdr(ptr); + } + } + globals = cdr(globals); + } + // Built-in? + int entries = 0, i; + for (i = 0; i < NumTables; i++) entries += Metatable[i].size; + for (i = 0; i < entries; i++) { + if (findsubstring(part, (builtin_t)i)) { + if (print) { + uint8_t ft = fntype(getminmax(i)); + pbuiltin((builtin_t)i, pserial); + pserial(' '); + pserial('('); + if (ft == FUNCTIONS) pfstring("function", pserial); + else if (ft == SPECIAL_FORMS) pfstring("special form", pserial); + else if (ft == SPECIAL_SYMBOLS) pfstring("special symbol", pserial); + else pfstring("symbol/keyword", pserial); + pserial(')'); + pln(pserial); + } else { + cdr(ptr) = cons(bsymbol(i), NULL); + ptr = cdr(ptr); + } + } + testescape(); + } + return cdr(result); +} + +/* + cstring - converts a Lisp string to a C string in buffer and returns buffer + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word +*/ +char* cstring(object* form, char* buffer, int buflen) { + form = cdr(checkstring(form)); + int index = 0; + while (form != NULL) { + int chars = form->integer; + for (int i = (sizeof(int) - 1) * 8; i >= 0; i = i - 8) { + char ch = chars >> i & 0xFF; + if (ch) { + if (index >= buflen - 1) error2("no room for string"); + buffer[index++] = ch; + } + } + form = car(form); + } + buffer[index] = '\0'; + return buffer; +} + +/* + ipstring - parses an IP address from a Lisp string and returns it as an IPAddress type (uint32_t) + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word +*/ +uint32_t ipstring(object* form) { + form = cdr(checkstring(form)); + int p = 0; + union { + uint32_t ipaddress; + uint8_t ipbytes[4]; + }; + ipaddress = 0; + while (form != NULL) { + int chars = form->integer; + for (int i = (sizeof(int) - 1) * 8; i >= 0; i = i - 8) { + char ch = chars >> i & 0xFF; + if (ch) { + if (ch == '.') { + p++; + if (p > 3) error("illegal IP address", form); + } else ipbytes[p] = (ipbytes[p] * 10) + ch - '0'; + } + } + form = car(form); + } + return ipaddress; +} + +// Lookup variable in environment + +object* value(symbol_t n, object* env) { + while (env != NULL) { + object* pair = car(env); + if (pair != NULL && car(pair)->name == n) return pair; + env = cdr(env); + } + return nil; +} + +/* + findpair - returns the (var . value) pair bound to variable var in the local or global environment +*/ +object* findpair(object* var, object* env) { + symbol_t name = var->name; + object* pair = value(name, env); + if (pair == NULL) pair = value(name, GlobalEnv); + return pair; +} + +/* + boundp - tests whether var is bound to a value +*/ +bool boundp(object* var, object* env) { + if (!symbolp(var)) error(notasymbol, var); + return (findpair(var, env) != NULL); +} + +/* + findvalue - returns the value bound to variable var, or gives an error if unbound +*/ +object* findvalue(object* var, object* env) { + object* pair = findpair(var, env); + if (pair == NULL) error("unknown variable", var); + return pair; +} + +// Handling closures + +object* closure(bool tc, symbol_t name, object* function, object* args, object** env) { + object* state = car(function); + function = cdr(function); + int trace = 0; + if (name) trace = tracing(name); + if (trace) { + indent(TraceDepth[trace - 1] << 1, ' ', pserial); + pint(TraceDepth[trace - 1]++, pserial); + pserial(':'); + pserial(' '); + pserial('('); + printsymbol(symbol(name), pserial); + } + object* params = first(function); + if (!listp(params)) errorsym(name, notalist, params); + function = cdr(function); + // Dropframe + if (tc) { + if (*env != NULL && car(*env) == NULL) { + pop(*env); + while (*env != NULL && car(*env) != NULL) pop(*env); + } else push(nil, *env); + } + // Push state + while (consp(state)) { + object* pair = first(state); + push(pair, *env); + state = cdr(state); + } + // Add arguments to environment + bool optional = false; + while (params != NULL) { + object* value; + object* var = first(params); + if (isbuiltin(var, OPTIONAL)) optional = true; + else { + if (consp(var)) { + if (!optional) errorsym(name, "invalid default value", var); + if (args == NULL) value = eval(second(var), *env); + else { + value = first(args); + args = cdr(args); + } + var = first(var); + if (!symbolp(var)) errorsym(name, "illegal optional parameter", var); + } else if (!symbolp(var)) { + errorsym(name, "illegal function parameter", var); + } else if (isbuiltin(var, AMPREST)) { + params = cdr(params); + var = first(params); + value = args; + args = NULL; + } else { + if (args == NULL) { + if (optional) value = nil; + else errorsym2(name, toofewargs); + } else { + value = first(args); + args = cdr(args); + } + } + push(cons(var, value), *env); + if (trace) { + pserial(' '); + printobject(value, pserial); + } + } + params = cdr(params); + } + if (args != NULL) errorsym2(name, toomanyargs); + if (trace) { + pserial(')'); + pln(pserial); + } + // Do an implicit progn + if (tc) push(nil, *env); + return sp_progn(function, *env); +} + +object* apply(object* function, object* args, object* env) { + if (symbolp(function)) error("can't call a symbol", function); + if (bfunctionp(function)) { + builtin_t fname = builtin(function->name); + if ((fname < ENDFUNCTIONS) && (fntype(getminmax(fname)) == FUNCTIONS)) { + Context = fname; + checkargs(args); + return ((fn_ptr_type)lookupfn(fname))(args, env); + } else function = eval(function, env); + } + if (consp(function) && isbuiltin(car(function), LAMBDA)) { + object* result = closure(false, sym(NIL), function, args, &env); + clrflag(TAILCALL); + return eval(result, env); + } + if (consp(function) && isbuiltin(car(function), CLOSURE)) { + function = cdr(function); + object* result = closure(false, sym(NIL), function, args, &env); + clrflag(TAILCALL); + return eval(result, env); + } + error("illegal function", function); + return NULL; +} + +// In-place operations + +/* + place - returns a pointer to an object referenced in the second argument of an + in-place operation such as setf. bit is used to indicate the bit position in a bit array +*/ +object** place(object* args, object* env, int* bit) { +PLACE: + *bit = -1; + if (atom(args)) return &cdr(findvalue(args, env)); + object* function = first(args); + if (symbolp(function)) { + symbol_t sname = function->name; + if (sname == sym(CAR) || sname == sym(FIRST)) { + object* value = eval(second(args), env); + if (!listp(value)) error(canttakecar, value); + return &car(value); + } + if (sname == sym(CDR) || sname == sym(REST)) { + object* value = eval(second(args), env); + if (!listp(value)) error(canttakecdr, value); + return &cdr(value); + } + if (sname == sym(NTH)) { + int index = checkinteger(eval(second(args), env)); + object* list = eval(third(args), env); + if (atom(list)) { + Context = NTH; + error("second argument is not a list", list); + } + int i = index; + while (i > 0) { + list = cdr(list); + if (list == NULL) { + Context = NTH; + error(indexrange, number(index)); + } + i--; + } + return &car(list); + } + if (sname == sym(CHAR)) { + int index = checkinteger(eval(third(args), env)); + object* string = checkstring(eval(second(args), env)); + object** loc = getcharplace(string, index, bit); + if ((*loc) == NULL || (((((*loc)->chars) >> ((-(*bit) - 2) << 3)) & 0xFF) == 0)) { + Context = CHAR; + error(indexrange, number(index)); + } + return loc; + } + if (sname == sym(AREF)) { + object* array = eval(second(args), env); + if (!arrayp(array)) { + Context = AREF; + error("first argument is not an array", array); + } + return getarray(array, cddr(args), env, bit); + } + } else if (is_macro_call(args, env)) { + function = eval(function, env); + goto PLACE; + } + error2("illegal place"); + return nil; +} + +// Checked car and cdr + +/* + carx - car with error checking +*/ +object* carx(object* arg) { + if (!listp(arg)) error(canttakecar, arg); + if (arg == nil) return nil; + return car(arg); +} + +/* + cdrx - cdr with error checking +*/ +object* cdrx(object* arg) { + if (!listp(arg)) error(canttakecdr, arg); + if (arg == nil) return nil; + return cdr(arg); +} + +/* + cxxxr - implements a general cxxxr function, + pattern is a sequence of bits 0b1xxx where x is 0 for a and 1 for d. +*/ +object* cxxxr(object* args, uint8_t pattern) { + object* arg = first(args); + while (pattern != 1) { + if ((pattern & 1) == 0) arg = carx(arg); + else arg = cdrx(arg); + pattern = pattern >> 1; + } + return arg; +} + +// Mapping helper functions + +/* + mapcl - handles either mapc when mapl=false, or mapl when mapl=true +*/ +object* mapcl(object* args, object* env, bool mapl) { + object* function = first(args); + args = cdr(args); + object* result = first(args); + protect(result); + object* params = cons(NULL, NULL); + protect(params); + // Make parameters + while (true) { + object* tailp = params; + object* lists = args; + while (lists != NULL) { + object* list = car(lists); + if (list == NULL) { + unprotect(); + unprotect(); + return result; + } + if (improperp(list)) error(notproper, list); + object* item = mapl ? list : first(list); + object* obj = cons(item, NULL); + car(lists) = cdr(list); + cdr(tailp) = obj; + tailp = obj; + lists = cdr(lists); + } + apply(function, cdr(params), env); + } +} + +/* + mapcarfun - function specifying how to combine the results in mapcar +*/ +void mapcarfun(object* result, object** tail) { + object* obj = cons(result, NULL); + cdr(*tail) = obj; + *tail = obj; +} + +/* + mapcanfun - function specifying how to combine the results in mapcan +*/ +void mapcanfun(object* result, object** tail) { + if (cdr(*tail) != NULL) error(notproper, *tail); + while (consp(result)) { + cdr(*tail) = result; + *tail = result; + result = cdr(result); + } +} + +/* + mapcarcan - function used by marcar and mapcan when maplist=false, and maplist when maplist=true + It takes the arguments, the env, a function specifying how the results are combined, and a bool. +*/ +object* mapcarcan(object* args, object* env, mapfun_t fun, bool maplist) { + object* function = first(args); + args = cdr(args); + object* params = cons(NULL, NULL); + protect(params); + object* head = cons(NULL, NULL); + protect(head); + object* tail = head; + // Make parameters + while (true) { + object* tailp = params; + object* lists = args; + while (lists != NULL) { + object* list = car(lists); + if (list == NULL) { + unprotect(); + unprotect(); + return cdr(head); + } + if (improperp(list)) error(notproper, list); + object* item = maplist ? list : first(list); + object* obj = cons(item, NULL); + car(lists) = cdr(list); + cdr(tailp) = obj; + tailp = obj; + lists = cdr(lists); + } + object* result = apply(function, cdr(params), env); + fun(result, &tail); + } +} + +/* + dobody - function used by do when star=false and do* when star=true +*/ +object* dobody(object* args, object* env, bool star) { + object* varlist = first(args); + object* endlist = second(args); + object* head = cons(NULL, NULL); + protect(head); + object* ptr = head; + object* newenv = env; + while (varlist != NULL) { + object* varform = first(varlist); + object* var; + object* init = NULL; + object* step = NULL; + if (atom(varform)) var = varform; + else { + var = first(varform); + varform = cdr(varform); + if (varform != NULL) { + init = eval(first(varform), env); + varform = cdr(varform); + if (varform != NULL) step = cons(first(varform), NULL); + } + } + object* pair = cons(var, init); + push(pair, newenv); + if (star) env = newenv; + object* cell = cons(cons(step, pair), NULL); + cdr(ptr) = cell; + ptr = cdr(ptr); + varlist = cdr(varlist); + } + env = newenv; + head = cdr(head); + object* endtest = first(endlist); + object* results = cdr(endlist); + while (eval(endtest, env) == NULL) { + object* forms = cddr(args); + while (forms != NULL) { + object* result = eval(car(forms), env); + if (tstflag(RETURNFLAG)) { + clrflag(RETURNFLAG); + return result; + } + forms = cdr(forms); + } + object* varlist = head; + int count = 0; + while (varlist != NULL) { + object* varform = first(varlist); + object* step = car(varform); + object* pair = cdr(varform); + if (step != NULL) { + object* val = eval(first(step), env); + if (star) { + cdr(pair) = val; + } else { + push(val, GCStack); + push(pair, GCStack); + count++; + } + } + varlist = cdr(varlist); + } + while (count > 0) { + cdr(car(GCStack)) = car(cdr(GCStack)); + pop(GCStack); + pop(GCStack); + count--; + } + } + unprotect(); + return progn_no_tc(results, env); +} + +// I2C interface for up to two ports, using Arduino Wire + +void I2Cinit(TwoWire* port, bool enablePullup) { + (void)enablePullup; + port->begin(); +} + +int I2Cread(TwoWire* port) { + return port->read(); +} + +void I2Cwrite(TwoWire* port, uint8_t data) { + port->write(data); +} + +bool I2Cstart(TwoWire* port, uint8_t address, uint8_t read) { + int ok = true; + if (read == 0) { + port->beginTransmission(address); + ok = (port->endTransmission(true) == 0); + port->beginTransmission(address); + } else port->requestFrom(address, I2Ccount); + return ok; +} + +bool I2Crestart(TwoWire* port, uint8_t address, uint8_t read) { + int error = (port->endTransmission(false) != 0); + if (read == 0) port->beginTransmission(address); + else port->requestFrom(address, I2Ccount); + return error ? false : true; +} + +void I2Cstop(TwoWire* port, uint8_t read) { + if (read == 0) port->endTransmission(); // Check for error? + // Release pins + port->end(); +} + +// Streams + +// Simplify board differences +#if defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) +#define ULISP_I2C1 +#endif + + +inline int spiread() { + return SPI.transfer(0); +} +inline int i2cread() { + return I2Cread(&Wire); +} +#if defined(ULISP_I2C1) +inline int i2c1read() { + return I2Cread(&Wire1); +} +#endif +inline int serial1read() { + while (!Serial1.available()) testescape(); + return Serial1.read(); +} +#if defined(sdcardsupport) +File SDpfile, SDgfile; +inline int SDread() { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + return SDgfile.read(); +} +#endif + +WiFiClient client; +WiFiServer server(80); + +inline int WiFiread() { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + while (!client.available()) testescape(); + return client.read(); +} + +void serialbegin(int address, int baud) { + if (address == 1) Serial1.begin((long)baud * 100); + else error("port not supported", number(address)); +} + +void serialend(int address) { + if (address == 1) { + Serial1.flush(); + Serial1.end(); + } +} + +gfun_t gstreamfun(object* args) { + int streamtype = SERIALSTREAM; + int address = 0; + gfun_t gfun = gserial; + if (args != NULL) { + int stream = isstream(first(args)); + streamtype = stream >> 8; + address = stream & 0xFF; + } + if (streamtype == I2CSTREAM) { + if (address < 128) gfun = i2cread; +#if defined(ULISP_I2C1) + else gfun = i2c1read; +#endif + } else if (streamtype == SPISTREAM) gfun = spiread; + else if (streamtype == SERIALSTREAM) { + if (address == 0) gfun = gserial; + else if (address == 1) gfun = serial1read; + } +#if defined(sdcardsupport) + else if (streamtype == SDSTREAM) + gfun = (gfun_t)SDread; +#endif + else if (streamtype == WIFISTREAM) gfun = (gfun_t)WiFiread; + else error2("unknown stream type"); + return gfun; +} + +inline void spiwrite(char c) { + SPI.transfer(c); +} +inline void i2cwrite(char c) { + I2Cwrite(&Wire, c); +} +#if defined(ULISP_I2C1) +inline void i2c1write(char c) { + I2Cwrite(&Wire1, c); +} +#endif +inline void serial1write(char c) { + Serial1.write(c); +} +inline void WiFiwrite(char c) { + client.write(c); +} +#if defined(sdcardsupport) +inline void SDwrite(char c) { + int w = SDpfile.write(c); + if (w != 1) { + Context = NIL; + error2("failed to write to file"); + } +} +#endif +#if defined(gfxsupport) +inline void gfxwrite(char c) { + tft.write(c); +} +#endif + +pfun_t pstreamfun(object* args) { + int streamtype = SERIALSTREAM; + int address = 0; + pfun_t pfun = pserial; + if (args != NULL && first(args) != NULL) { + int stream = isstream(first(args)); + streamtype = stream >> 8; + address = stream & 0xFF; + } + if (streamtype == I2CSTREAM) { + if (address < 128) pfun = i2cwrite; +#if defined(ULISP_I2C1) + else pfun = i2c1write; +#endif + } else if (streamtype == SPISTREAM) pfun = spiwrite; + else if (streamtype == SERIALSTREAM) { + if (address == 0) pfun = pserial; + else if (address == 1) pfun = serial1write; + } else if (streamtype == STRINGSTREAM) { + pfun = pstr; + } +#if defined(sdcardsupport) + else if (streamtype == SDSTREAM) + pfun = (pfun_t)SDwrite; +#endif +#if defined(gfxsupport) + else if (streamtype == GFXSTREAM) pfun = (pfun_t)gfxwrite; +#endif + else if (streamtype == WIFISTREAM) pfun = (pfun_t)WiFiwrite; + else error2("unknown stream type"); + return pfun; +} + +// Check pins + +void checkanalogread(int pin) { + + // if (!(pin==0 || pin==2 || pin==4 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=36) || pin==39)) + // error("invalid pin", number(pin)); + (void)pin; +} + +void checkanalogwrite(int pin) { +#ifdef toneimplemented + // ERROR PWM channel unavailable on pin requested! 1 + // PWM available on: 2,4,5,12-19,21-23,25-27,32-33 + if (!(pin == 2 || pin == 4 || pin == 5 || (pin >= 12 && pin <= 19) || (pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || pin == 32 || pin == 33)) error("not a PWM-capable pin", number(pin)); +#else + if (!(pin >= 25 && pin <= 26)) error("not a DAC pin", number(pin)); +#endif +} + +// Note + +const int scale[] = { 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902 }; + +void playnote(int pin, int note, int octave) { +#ifdef toneimplemented + int oct = octave + note / 12; + int prescaler = 8 - oct; + if (prescaler < 0 || prescaler > 8) error("octave out of range", number(prescaler)); + tone((uint8_t)pin, scale[note % 12] >> prescaler); +#else + error2("not available"); +#endif +} + +void nonote(int pin) { +#ifdef toneimplemented + noTone(pin); +#else + error2("not available"); +#endif +} + +// Sleep + +void initsleep() {} + +void doze(int secs) { + delay(1000 * secs); +} + +// Prettyprint + +const int PPINDENT = 2; +const int PPWIDTH = 80; +const int GFXPPWIDTH = 52; // 320 pixel wide screen +int ppwidth = PPWIDTH; + +void pcount(char c) { + if (c == '\n') PrintCount++; + PrintCount++; +} + +/* + atomwidth - calculates the character width of an atom +*/ +uint8_t atomwidth(object* obj) { + PrintCount = 0; + printobject(obj, pcount); + return PrintCount; +} + +/* + basewidth - calculates the character width of an integer printed in a given base +*/ +uint8_t basewidth(object* obj, uint8_t base) { + PrintCount = 0; + pintbase(obj->integer, base, pcount); + return PrintCount; +} + +/* + quoted - tests whether an object is quoted with the right quote type +*/ +bool quoted(object* obj, builtin_t which) { + return (consp(obj) && car(obj) != NULL && car(obj)->name == sym(which) && consp(cdr(obj)) && cddr(obj) == NULL); +} + +/* + subwidth - returns the space left from w after printing object +*/ +int subwidth(object* obj, int w) { + if (atom(obj)) return w - atomwidth(obj); + if (quoted(obj, QUOTE) || quoted(obj, BACKQUOTE) || quoted(obj, UNQUOTE) || quoted(obj, UNQUOTE_SPLICING)) { + if (builtin(car(obj)->name) == UNQUOTE_SPLICING) w--; // unquote splicing is 2 chars + obj = car(cdr(obj)); + } + return subwidthlist(obj, w - 1); +} + +/* + subwidth - returns the space left from w after printing a list +*/ +int subwidthlist(object* form, int w) { + while (form != NULL && w >= 0) { + if (atom(form)) return w - (2 + atomwidth(form)); + w = subwidth(car(form), w - 1); + form = cdr(form); + } + return w; +} + +/* + superprint - handles pretty-printing +*/ +void superprint(object* form, int lm, pfun_t pfun) { + if (atom(form)) { + if (symbolp(form) && form->name == sym(NOTHING)) printsymbol(form, pfun); + else printobject(form, pfun); + } else if (quoted(form, QUOTE)) { + pfun('\''); + superprint(car(cdr(form)), lm + 1, pfun); + } else if (quoted(form, BACKQUOTE)) { + pfun('`'); + superprint(car(cdr(form)), lm + 1, pfun); + } else if (quoted(form, UNQUOTE)) { + pfun(','); + superprint(car(cdr(form)), lm + 1, pfun); + } else if (quoted(form, UNQUOTE_SPLICING)) { + pfun(','); + pfun('@'); + superprint(car(cdr(form)), lm + 2, pfun); + } else { + lm = lm + PPINDENT; + bool fits = (subwidth(form, ppwidth - lm - PPINDENT) >= 0); + int special = 0, extra = 0; + bool separate = true; + object* arg = car(form); + if (symbolp(arg) && builtinp(arg->name)) { + uint8_t minmax = getminmax(builtin(arg->name)); + if (minmax == 0327 || minmax == 0313) special = 2; // defun, setq, setf, defvar + else if (minmax == 0317 || minmax == 0017 || minmax == 0117 || minmax == 0123) special = 1; + } + while (form != NULL) { + if (atom(form)) { + pfstring(" . ", pfun); + printobject(form, pfun); + pfun(')'); + return; + } else if (separate) { + pfun('('); + separate = false; + } else if (special) { + pfun(' '); + special--; + } else if (fits) { + pfun(' '); + } else { + pln(pfun); + indent(lm, ' ', pfun); + } + superprint(car(form), lm + extra, pfun); + form = cdr(form); + } + pfun(')'); + } +} + +/* + edit - the Lisp tree editor + Steps through a function definition, editing it a bit at a time, using single-key editing commands. +*/ +object* edit(object* fun) { + while (1) { + if (tstflag(EXITEDITOR)) return fun; + char c = gserial(); + if (c == 'q') setflag(EXITEDITOR); + else if (c == 'b') return fun; + else if (c == 'r') fun = read(gserial); + else if (c == '\n') { + pfl(pserial); + superprint(fun, 0, pserial); + pln(pserial); + } else if (c == 'c') fun = cons(read(gserial), fun); + else if (atom(fun)) pserial('!'); + else if (c == 'd') fun = cons(car(fun), edit(cdr(fun))); + else if (c == 'a') fun = cons(edit(car(fun)), cdr(fun)); + else if (c == 'x') fun = cdr(fun); + else pserial('?'); + } +} + +// Special forms + +object* sp_quote(object* args, object* env) { + (void)env; + return first(args); +} + +/* + (or item*) + Evaluates its arguments until one returns non-nil, and returns its value. +*/ +object* sp_or(object* args, object* env) { + while (args != NULL) { + object* val = eval(car(args), env); + if (val != NULL) return val; + args = cdr(args); + } + return nil; +} + +// Need to do manual search because findvalue() uses eq() but we need equal() for this. +object* find_setf_func(object* whatenv, object* funcname) { + object* what = cons(bsymbol(SETF), cons(funcname, nil)); + for (object* z = whatenv; z != nil; z = cdr(z)) { + object* pair = car(z); + if (equal(what, car(pair))) return pair; + } + return nil; +} + +/* + (defun name (parameters) form*) + Defines a function. +*/ +object* sp_defun(object* args, object* env) { + (void)env; + object* var = first(args); + if (!symbolp(var)) { + // Check for (setf foo) forms + if (consp(var) && listlength(var) == 2 && eq(first(var), bsymbol(SETF))) /* do nothing */ + ; + else error(notasymbol, var); + } + object* val = cons(bsymbol(LAMBDA), cdr(args)); + object* pair = value(var->name, GlobalEnv); + if (consp(var) && !pair) pair = find_setf_func(GlobalEnv, second(var)); + if (pair != NULL) cdr(pair) = val; + else push(cons(var, val), GlobalEnv); + return var; +} + +/* + (defvar variable form) + Defines a global variable. +*/ +object* sp_defvar(object* args, object* env) { + object* var = first(args); + if (!symbolp(var)) error(notasymbol, var); + object* val = NULL; + args = cdr(args); + if (args != NULL) { + setflag(NOESC); + val = eval(first(args), env); + clrflag(NOESC); + } + object* pair = value(var->name, GlobalEnv); + if (pair != NULL) cdr(pair) = val; + else push(cons(var, val), GlobalEnv); + return var; +} + +/* + (defmacro name (parameters) form*) + Defines a syntactic macro. +*/ +object* sp_defmacro(object* args, object* env) { + (void)env; + object* var = first(args); + if (!symbolp(var)) error(notasymbol, var); + object* val = cons(bsymbol(MACRO), cdr(args)); + object* pair = value(var->name, GlobalEnv); + if (pair != NULL) cdr(pair) = val; + else push(cons(var, val), GlobalEnv); + return var; +} + +/* + (setq symbol value [symbol value]*) + For each pair of arguments assigns the value of the second argument + to the variable specified in the first argument. +*/ +object* sp_setq(object* args, object* env) { + object* arg = nil; + while (args != NULL) { + if (cdr(args) == NULL) error2(oddargs); + object* pair = findvalue(first(args), env); + arg = eval(second(args), env); + cdr(pair) = arg; + args = cddr(args); + } + return arg; +} + +/* + (loop forms*) + Executes its arguments repeatedly until one of the arguments calls (return), + which then causes an exit from the loop. +*/ +object* sp_loop(object* args, object* env) { + object* start = args; + for (;;) { + yield(); + args = start; + while (args != NULL) { + object* result = eval(car(args), env); + if (tstflag(RETURNFLAG)) { + clrflag(RETURNFLAG); + return result; + } + args = cdr(args); + } + } +} + +/* + (return [value]) + Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value. +*/ +object* fn_return(object* args, object* env) { + setflag(RETURNFLAG); + return args ? first(args) : nil; +} + +/* + (push item place) + Modifies the value of place, which should be a list, to add item onto the front of the list, + and returns the new list. +*/ +object* sp_push(object* args, object* env) { + int bit; + object* item = eval(first(args), env); + object** loc = place(second(args), env, &bit); + if (bit != -1) error2(invalidarg); + push(item, *loc); + return *loc; +} + +/* + (pop place) + Modifies the value of place, which should be a non-nil list, to remove its first item, and returns that item. +*/ +object* sp_pop(object* args, object* env) { + int bit; + object* arg = first(args); + if (arg == NULL) error2(invalidarg); + object** loc = place(arg, env, &bit); + if (bit < -1) error(invalidarg, arg); + if (!consp(*loc)) error(notalist, *loc); + object* result = car(*loc); + pop(*loc); + return result; +} + +// Accessors + +/* + (incf place [number]) + Increments a place, which should have an numeric value, and returns the result. + The third argument is an optional increment which defaults to 1. +*/ +object* sp_incf(object* args, object* env) { + int bit; + object** loc = place(first(args), env, &bit); + if (bit < -1) error2(notanumber); + args = cdr(args); + + object* x = *loc; + object* inc = (args != NULL) ? eval(first(args), env) : NULL; + + if (bit != -1) { + int increment; + if (inc == NULL) increment = 1; + else increment = checkbitvalue(inc); + int newvalue = (((*loc)->integer) >> bit & 1) + increment; + + if (newvalue & ~1) error2("result is not a bit value"); + *loc = number((((*loc)->integer) & ~(1 << bit)) | newvalue << bit); + return number(newvalue); + } + + if (floatp(x) || floatp(inc)) { + float increment; + float value = checkintfloat(x); + + if (inc == NULL) increment = 1.0; + else increment = checkintfloat(inc); + + *loc = makefloat(value + increment); + } else if (integerp(x) && (integerp(inc) || inc == NULL)) { + int increment; + int value = x->integer; + + if (inc == NULL) increment = 1; + else increment = inc->integer; + + if (increment < 1) { + if (INT_MIN - increment > value) *loc = makefloat((float)value + (float)increment); + else *loc = number(value + increment); + } else { + if (INT_MAX - increment < value) *loc = makefloat((float)value + (float)increment); + else *loc = number(value + increment); + } + } else error2(notanumber); + return *loc; +} + +/* + (decf place [number]) + Decrements a place, which should have an numeric value, and returns the result. + The third argument is an optional decrement which defaults to 1. +*/ +object* sp_decf(object* args, object* env) { + int bit; + object** loc = place(first(args), env, &bit); + if (bit < -1) error2(notanumber); + args = cdr(args); + + object* x = *loc; + object* dec = (args != NULL) ? eval(first(args), env) : NULL; + + if (bit != -1) { + int decrement; + if (dec == NULL) decrement = 1; + else decrement = checkbitvalue(dec); + int newvalue = (((*loc)->integer) >> bit & 1) - decrement; + + if (newvalue & ~1) error2("result is not a bit value"); + *loc = number((((*loc)->integer) & ~(1 << bit)) | newvalue << bit); + return number(newvalue); + } + + if (floatp(x) || floatp(dec)) { + float decrement; + float value = checkintfloat(x); + + if (dec == NULL) decrement = 1.0; + else decrement = checkintfloat(dec); + + *loc = makefloat(value - decrement); + } else if (integerp(x) && (integerp(dec) || dec == NULL)) { + int decrement; + int value = x->integer; + + if (dec == NULL) decrement = 1; + else decrement = dec->integer; + + if (decrement < 1) { + if (INT_MAX + decrement < value) *loc = makefloat((float)value - (float)decrement); + else *loc = number(value - decrement); + } else { + if (INT_MIN + decrement > value) *loc = makefloat((float)value - (float)decrement); + else *loc = number(value - decrement); + } + } else error2(notanumber); + return *loc; +} + +/* + (setf place value [place value]*) + For each pair of arguments modifies a place to the result of evaluating value. +*/ +object* sp_setf(object* args, object* env) { + int bit; + object* arg = nil; + object* placeform = nil; + object** loc; + while (args != NULL) { + if (cdr(args) == NULL) error2(oddargs); + placeform = first(args); + // Check for special defsetf forms first before calling place() + if (consp(placeform)) { + object* funcname = first(placeform); + object* userdef = find_setf_func(env, funcname); + if (!userdef) userdef = find_setf_func(GlobalEnv, funcname); + if (userdef) { + // usercode should be a lambda + arg = eval(cons(cdr(userdef), cons(second(args), rest(placeform))), env); + goto next; + } + } + arg = eval(second(args), env); + loc = place(placeform, env, &bit); + if (bit == -1) *loc = arg; + else if (bit < -1) (*loc)->chars = ((*loc)->chars & ~(0xff << ((-bit - 2) << 3))) | checkchar(arg) << ((-bit - 2) << 3); + else *loc = number((checkinteger(*loc) & ~(1 << bit)) | checkbitvalue(arg) << bit); +next: + args = cddr(args); + } + return arg; +} + +// Other special forms + +/* + (dolist (var list [result]) form*) + Sets the local variable var to each element of list in turn, and executes the forms. + It then returns result, or nil if result is omitted. +*/ +object* sp_dolist(object* args, object* env) { + object* params = checkarguments(args, 2, 3); + object* var = first(params); + object* list = eval(second(params), env); + protect(list); // Don't GC the list + object* pair = cons(var, nil); + push(pair, env); + params = cddr(params); + args = cdr(args); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + cdr(pair) = first(list); + object* forms = args; + while (forms != NULL) { + object* result = eval(car(forms), env); + if (tstflag(RETURNFLAG)) { + clrflag(RETURNFLAG); + unprotect(); + return result; + } + forms = cdr(forms); + } + list = cdr(list); + } + cdr(pair) = nil; + unprotect(); + if (params == NULL) return nil; + return eval(car(params), env); +} + +/* + (dotimes (var number [result]) form*) + Executes the forms number times, with the local variable var set to each integer from 0 to number-1 in turn. + It then returns result, or nil if result is omitted. +*/ +object* sp_dotimes(object* args, object* env) { + if (args == NULL || listlength(first(args)) < 2) error2(noargument); + object* params = first(args); + object* var = first(params); + int count = checkinteger(eval(second(params), env)); + int index = 0; + params = cddr(params); + object* pair = cons(var, number(0)); + push(pair, env); + args = cdr(args); + while (index < count) { + cdr(pair) = number(index); + object* forms = args; + while (forms != NULL) { + object* result = eval(car(forms), env); + if (tstflag(RETURNFLAG)) { + clrflag(RETURNFLAG); + return result; + } + forms = cdr(forms); + } + index++; + } + cdr(pair) = number(index); + if (params == NULL) return nil; + return eval(car(params), env); +} + +/* + (do ((var [init [step]])*) (end-test result*) form*) + Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step sequentially. + The forms are executed until end-test is true. It returns result. +*/ +object* sp_do(object* args, object* env) { + return dobody(args, env, false); +} + +/* + (do* ((var [init [step]])*) (end-test result*) form*) + Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step in parallel. + The forms are executed until end-test is true. It returns result. +*/ +object* sp_dostar(object* args, object* env) { + return dobody(args, env, true); +} + +/* + (trace [function]*) + Turns on tracing of up to TRACEMAX user-defined functions, + and returns a list of the functions currently being traced. +*/ +object* sp_trace(object* args, object* env) { + (void)env; + while (args != NULL) { + object* var = first(args); + if (!symbolp(var)) error(notasymbol, var); + trace(var->name); + args = cdr(args); + } + int i = 0; + while (i < TRACEMAX) { + if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args); + i++; + } + return args; +} + +/* + (untrace [function]*) + Turns off tracing of up to TRACEMAX user-defined functions, and returns a list of the functions untraced. + If no functions are specified it untraces all functions. +*/ +object* sp_untrace(object* args, object* env) { + (void)env; + if (args == NULL) { + int i = 0; + while (i < TRACEMAX) { + if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args); + TraceFn[i] = 0; + i++; + } + } else { + while (args != NULL) { + object* var = first(args); + if (!symbolp(var)) error(notasymbol, var); + untrace(var->name); + args = cdr(args); + } + } + return args; +} + +/* + (for-millis ([number]) form*) + Executes the forms and then waits until a total of number milliseconds have elapsed. + Returns the total number of milliseconds taken. +*/ +object* sp_formillis(object* args, object* env) { + object* param = checkarguments(args, 0, 1); + unsigned long start = millis(); + unsigned long now, total = 0; + if (param != NULL) total = checkinteger(eval(first(param), env)); + progn_no_tc(cdr(args), env); + do { + now = millis() - start; + testescape(); + } while (now < total); + if (now <= INT_MAX) return number(now); + return nil; +} + +/* + (time form) + Prints the value returned by the form, and the time taken to evaluate the form + in milliseconds or seconds. +*/ +object* sp_time(object* args, object* env) { + unsigned long start = millis(); + object* result = eval(first(args), env); + unsigned long elapsed = millis() - start; + printobject(result, pserial); + pfstring("\nTime: ", pserial); + if (elapsed < 1000) { + pint(elapsed, pserial); + pfstring(" ms\n", pserial); + } else { + elapsed = elapsed + 50; + pint(elapsed / 1000, pserial); + pserial('.'); + pint((elapsed / 100) % 10, pserial); + pfstring(" s\n", pserial); + } + return bsymbol(NOTHING); +} + +/* + (with-output-to-string (str) form*) + Returns a string containing the output to the stream variable str. +*/ +object* sp_withoutputtostring(object* args, object* env) { + object* params = checkarguments(args, 1, 1); + if (params == NULL) error2(nostream); + object* var = first(params); + object* pair = cons(var, stream(STRINGSTREAM, 0)); + push(pair, env); + object* string = startstring(); + protect(string); + object* forms = cdr(args); + progn_no_tc(forms, env); + unprotect(); + return string; +} + +/* + (with-serial (str port [baud]) form*) + Evaluates the forms with str bound to a serial-stream using port. + The optional baud gives the baud rate divided by 100, default 96. +*/ +object* sp_withserial(object* args, object* env) { + object* params = checkarguments(args, 2, 3); + object* var = first(params); + int address = checkinteger(eval(second(params), env)); + params = cddr(params); + int baud = 96; + if (params != NULL) baud = checkinteger(eval(first(params), env)); + object* pair = cons(var, stream(SERIALSTREAM, address)); + push(pair, env); + serialbegin(address, baud); + object* forms = cdr(args); + object* result = progn_no_tc(forms, env); + serialend(address); + return result; +} + +/* + (with-i2c (str [port] address [read-p]) form*) + Evaluates the forms with str bound to an i2c-stream defined by address. + If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes + to be read from the stream. If port is omitted it defaults to 0, otherwise it specifies the port, 0 or 1. +*/ +object* sp_withi2c(object* args, object* env) { + object* params = checkarguments(args, 2, 4); + object* var = first(params); + object* addr = eval(second(params), env); + int address = checkinteger(addr); + params = cddr(params); + if ((address == 0 || address == 1) && params != NULL) { + address = address * 128 + checkinteger(eval(first(params), env)); + params = cdr(params); + } + int read = 0; // Write + I2Ccount = 0; + if (params != NULL) { + object* rw = eval(first(params), env); + if (integerp(rw)) I2Ccount = rw->integer; + read = (rw != NULL); + } + // Top bit of address is I2C port + TwoWire* port = &Wire; +#if defined(ULISP_I2C1) + if (address > 127) port = &Wire1; +#endif + I2Cinit(port, 1); // Pullups + object* pair = cons(var, (I2Cstart(port, address & 0x7F, read)) ? stream(I2CSTREAM, address) : nil); + push(pair, env); + object* forms = cdr(args); + object* result = progn_no_tc(forms, env); + I2Cstop(port, read); + return result; +} + +/* + (with-spi (str pin [clock] [bitorder] [mode]) form*) + Evaluates the forms with str bound to an spi-stream. + The parameters specify the enable pin, clock in kHz (default 4000), + bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), and SPI mode (default 0). +*/ +object* sp_withspi(object* args, object* env) { + object* params = checkarguments(args, 2, 6); + object* var = first(params); + params = cdr(params); + if (params == NULL) error2(nostream); + int pin = checkinteger(eval(car(params), env)); + pinMode(pin, OUTPUT); + digitalWrite(pin, HIGH); + params = cdr(params); + int clock = 4000, mode = SPI_MODE0; // Defaults + int bitorder = MSBFIRST; + if (params != NULL) { + clock = checkinteger(eval(car(params), env)); + params = cdr(params); + if (params != NULL) { + bitorder = (checkinteger(eval(car(params), env)) == 0) ? LSBFIRST : MSBFIRST; + params = cdr(params); + if (params != NULL) { + int modeval = checkinteger(eval(car(params), env)); + mode = (modeval == 3) ? SPI_MODE3 : (modeval == 2) ? SPI_MODE2 + : (modeval == 1) ? SPI_MODE1 + : SPI_MODE0; + } + } + } + object* pair = cons(var, stream(SPISTREAM, pin)); + push(pair, env); + SPI.begin(); + SPI.beginTransaction(SPISettings(((unsigned long)clock * 1000), bitorder, mode)); + digitalWrite(pin, LOW); + object* forms = cdr(args); + object* result = progn_no_tc(forms, env); + digitalWrite(pin, HIGH); + SPI.endTransaction(); + return result; +} + +/* + (with-sd-card (str filename [mode]) form*) + Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename. + If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite. +*/ +object* sp_withsdcard(object* args, object* env) { +#if defined(sdcardsupport) + object* params = checkarguments(args, 2, 3); + object* var = first(params); + params = cdr(params); + if (params == NULL) error2("no filename specified"); + builtin_t temp = Context; + object* filename = eval(first(params), env); + Context = temp; + if (!stringp(filename)) error("filename is not a string", filename); + params = cdr(params); + SD.begin(); + int mode = 0; + if (params != NULL && first(params) != NULL) mode = checkinteger(first(params)); + const char* oflag = FILE_READ; + if (mode == 1) oflag = FILE_APPEND; + else if (mode == 2) oflag = FILE_WRITE; + if (mode >= 1) { + char buffer[BUFFERSIZE]; + SDpfile = SD.open(MakeFilename(filename, buffer), oflag); + if (!SDpfile) error("problem writing to SD card or invalid filename", filename); + } else { + char buffer[BUFFERSIZE]; + SDgfile = SD.open(MakeFilename(filename, buffer), oflag); + if (!SDgfile) error("problem reading from SD card or invalid filename", filename); + } + object* pair = cons(var, stream(SDSTREAM, 1)); + push(pair, env); + object* forms = cdr(args); + object* result = progn_no_tc(forms, env); + if (mode >= 1) SDpfile.close(); + else SDgfile.close(); + return result; +#else + (void)args, (void)env; + error2("not supported"); + return nil; +#endif +} + +// Tail-recursive forms + +/* + (progn form*) + Evaluates several forms grouped together into a block, and returns the result of evaluating the last form. +*/ +object* sp_progn(object* args, object* env) { + if (args == NULL) return nil; + object* more = cdr(args); + while (more != NULL) { + object* result = eval(car(args), env); + if (tstflag(RETURNFLAG)) return result; + args = more; + more = cdr(args); + } + setflag(TAILCALL); + return car(args); +} + +object* progn_no_tc(object* args, object* env) { + object* value = sp_progn(args, env); + if (tstflag(TAILCALL)) { + clrflag(TAILCALL); + value = eval(value, env); + } + return value; +} + +/* + (if test then [else]) + Evaluates test. If it's non-nil the form then is evaluated and returned; + otherwise the form else is evaluated and returned. +*/ +object* sp_if(object* args, object* env) { + if (args == NULL || cdr(args) == NULL) error2(toofewargs); + if (eval(first(args), env) != nil) { + setflag(TAILCALL); + return second(args); + } + args = cddr(args); + if (args) { + setflag(TAILCALL); + return first(args); + } + return nil; +} + +/* + (cond ((test form*) (test form*) ... )) + Each argument is a list consisting of a test optionally followed by one or more forms. + If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond. + If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way. +*/ +object* sp_cond(object* args, object* env) { + while (args != NULL) { + object* clause = first(args); + if (!consp(clause)) error(illegalclause, clause); + object* test = eval(first(clause), env); + object* forms = cdr(clause); + if (test != nil) { + if (forms == NULL) return test; + else return sp_progn(forms, env); + } + args = cdr(args); + } + return nil; +} + +/* + (when test form*) + Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned. +*/ +object* sp_when(object* args, object* env) { + if (args == NULL) error2(noargument); + if (eval(first(args), env) != nil) return sp_progn(cdr(args), env); + else return nil; +} + +/* + (unless test form*) + Evaluates the test. If it's nil the forms are evaluated and the last value is returned. +*/ +object* sp_unless(object* args, object* env) { + if (args == NULL) error2(noargument); + if (eval(first(args), env) != nil) return nil; + else return sp_progn(cdr(args), env); +} + +/* + (case keyform ((key form*) (key form*) ... )) + Evaluates a keyform to produce a test key, and then tests this against a series of arguments, + each of which is a list containing a key optionally followed by one or more forms. +*/ +object* sp_case(object* args, object* env) { + object* test = eval(first(args), env); + args = cdr(args); + while (args != NULL) { + object* clause = first(args); + if (!consp(clause)) error(illegalclause, clause); + object* key = car(clause); + object* forms = cdr(clause); + if (consp(key)) { + while (key != NULL) { + if (eq(test, car(key))) return sp_progn(forms, env); + key = cdr(key); + } + } else if (eq(test, key) || eq(key, tee)) return sp_progn(forms, env); + args = cdr(args); + } + return nil; +} + +/* + (and item*) + Evaluates its arguments until one returns nil, and returns the last value. +*/ +object* sp_and(object* args, object* env) { + if (args == NULL) return tee; + object* more = cdr(args); + while (more != NULL) { + if (eval(car(args), env) == NULL) return nil; + args = more; + more = cdr(args); + } + setflag(TAILCALL); + return car(args); +} + +// Core functions + +/* + (not item) + Returns t if its argument is nil, or nil otherwise. Equivalent to null. +*/ +object* fn_not(object* args, object* env) { + (void)env; + return (first(args) == nil) ? tee : nil; +} + +/* + (cons item item) + If the second argument is a list, cons returns a new list with item added to the front of the list. + If the second argument isn't a list cons returns a dotted pair. +*/ +object* fn_cons(object* args, object* env) { + (void)env; + return cons(first(args), second(args)); +} + +/* + (atom item) + Returns t if its argument is a single number, symbol, or nil. +*/ +object* fn_atom(object* args, object* env) { + (void)env; + return atom(first(args)) ? tee : nil; +} + +/* + (listp item) + Returns t if its argument is a list. +*/ +object* fn_listp(object* args, object* env) { + (void)env; + return listp(first(args)) ? tee : nil; +} + +/* + (consp item) + Returns t if its argument is a non-null list. +*/ +object* fn_consp(object* args, object* env) { + (void)env; + return consp(first(args)) ? tee : nil; +} + +/* + (symbolp item) + Returns t if its argument is a symbol. +*/ +object* fn_symbolp(object* args, object* env) { + (void)env; + object* arg = first(args); + return (arg == NULL || symbolp(arg)) ? tee : nil; +} + +/* + (arrayp item) + Returns t if its argument is an array. +*/ +object* fn_arrayp(object* args, object* env) { + (void)env; + return arrayp(first(args)) ? tee : nil; +} + +/* + (boundp item) + Returns t if its argument is a symbol with a value. +*/ +object* fn_boundp(object* args, object* env) { + return boundp(first(args), env) ? tee : nil; +} + +/* + (keywordp item) + Returns t if its argument is a keyword. +*/ +object* fn_keywordp(object* args, object* env) { + (void)env; + if (!symbolp(first(args))) return nil; + return keywordp(first(args)) ? tee : nil; +} + +/* + (set symbol value [symbol value]*) + For each pair of arguments, assigns the value of the second argument to the value of the first argument. +*/ +object* fn_setfn(object* args, object* env) { + object* arg = nil; + while (args != NULL) { + if (cdr(args) == NULL) error2(oddargs); + object* pair = findvalue(first(args), env); + arg = second(args); + cdr(pair) = arg; + args = cddr(args); + } + return arg; +} + +/* + (streamp item) + Returns t if its argument is a stream. +*/ +object* fn_streamp(object* args, object* env) { + (void)env; + object* arg = first(args); + return streamp(arg) ? tee : nil; +} + +/* + (eq item item) + Tests whether the two arguments are the same symbol, same character, equal numbers, + or point to the same cons, and returns t or nil as appropriate. +*/ +object* fn_eq(object* args, object* env) { + (void)env; + return eq(first(args), second(args)) ? tee : nil; +} + +/* + (equal item item) + Tests whether the two arguments are the same symbol, same character, equal numbers, + or point to the same cons, and returns t or nil as appropriate. +*/ +object* fn_equal(object* args, object* env) { + (void)env; + return equal(first(args), second(args)) ? tee : nil; +} + +// List functions + +/* + (car list) + Returns the first item in a list. +*/ +object* fn_car(object* args, object* env) { + (void)env; + return carx(first(args)); +} + +/* + (cdr list) + Returns a list with the first item removed. +*/ +object* fn_cdr(object* args, object* env) { + (void)env; + return cdrx(first(args)); +} + +/* + (caar list) +*/ +object* fn_caar(object* args, object* env) { + (void)env; + return cxxxr(args, 0b100); +} + +/* + (cadr list) +*/ +object* fn_cadr(object* args, object* env) { + (void)env; + return cxxxr(args, 0b101); +} + +/* + (cdar list) + Equivalent to (cdr (car list)). +*/ +object* fn_cdar(object* args, object* env) { + (void)env; + return cxxxr(args, 0b110); +} + +/* + (cddr list) + Equivalent to (cdr (cdr list)). +*/ +object* fn_cddr(object* args, object* env) { + (void)env; + return cxxxr(args, 0b111); +} + +/* + (caaar list) + Equivalent to (car (car (car list))). +*/ +object* fn_caaar(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1000); +} + +/* + (caadr list) + Equivalent to (car (car (cdar list))). +*/ +object* fn_caadr(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1001); + ; +} + +/* + (cadar list) + Equivalent to (car (cdr (car list))). +*/ +object* fn_cadar(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1010); +} + +/* + (caddr list) + Equivalent to (car (cdr (cdr list))). +*/ +object* fn_caddr(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1011); +} + +/* + (cdaar list) + Equivalent to (cdar (car (car list))). +*/ +object* fn_cdaar(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1100); +} + +/* + (cdadr list) + Equivalent to (cdr (car (cdr list))). +*/ +object* fn_cdadr(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1101); +} + +/* + (cddar list) + Equivalent to (cdr (cdr (car list))). +*/ +object* fn_cddar(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1110); +} + +/* + (cdddr list) + Equivalent to (cdr (cdr (cdr list))). +*/ +object* fn_cdddr(object* args, object* env) { + (void)env; + return cxxxr(args, 0b1111); +} + +/* + (length item) + Returns the number of items in a list, the length of a string, or the length of a one-dimensional array. +*/ +object* fn_length(object* args, object* env) { + (void)env; + object* arg = first(args); + if (listp(arg)) return number(listlength(arg)); + if (stringp(arg)) return number(stringlength(arg)); + if (!(arrayp(arg) && cdr(cddr(arg)) == NULL)) error("argument is not a list, 1d array, or string", arg); + return number(abs(first(cddr(arg))->integer)); +} + +/* + (array-dimensions item) + Returns a list of the dimensions of an array. +*/ +object* fn_arraydimensions(object* args, object* env) { + (void)env; + object* array = first(args); + if (!arrayp(array)) error("argument is not an array", array); + object* dimensions = cddr(array); + return (first(dimensions)->integer < 0) ? cons(number(-(first(dimensions)->integer)), cdr(dimensions)) : dimensions; +} + +/* + (list item*) + Returns a list of the values of its arguments. +*/ +object* fn_list(object* args, object* env) { + (void)env; + return args; +} + +/* + (copy-list list) + Returns a copy of a list. +*/ +object* fn_copylist(object* args, object* env) { + (void)env; + object* arg = first(args); + if (!listp(arg)) error(notalist, arg); + object* result = cons(NULL, NULL); + object* ptr = result; + while (arg != NULL) { + cdr(ptr) = cons(car(arg), NULL); + ptr = cdr(ptr); + arg = cdr(arg); + } + return cdr(result); +} + +/* + (make-array size [:initial-element element] [:element-type 'bit]) + If size is an integer it creates a one-dimensional array with elements from 0 to size-1. + If size is a list of n integers it creates an n-dimensional array with those dimensions. + If :element-type 'bit is specified the array is a bit array. +*/ +object* fn_makearray(object* args, object* env) { + (void)env; + object* def = nil; + bool bitp = false; + object* dims = first(args); + if (dims == NULL) error2("dimensions can't be nil"); + else if (atom(dims)) dims = cons(dims, NULL); + args = cdr(args); + while (args != NULL && cdr(args) != NULL) { + object* var = first(args); + if (isbuiltin(first(args), INITIALELEMENT)) def = second(args); + else if (isbuiltin(first(args), ELEMENTTYPE) && isbuiltin(second(args), BIT)) bitp = true; + else error("argument not recognized", var); + args = cddr(args); + } + if (bitp) { + if (def == nil) def = number(0); + else def = number(-checkbitvalue(def)); // 1 becomes all ones + } + return makearray(dims, def, bitp); +} + +/* + (reverse list) + Returns a list with the elements of list in reverse order. +*/ +object* fn_reverse(object* args, object* env) { + (void)env; + object* list = first(args); + object* result = NULL; + while (list != NULL) { + if (improperp(list)) error(notproper, list); + push(first(list), result); + list = cdr(list); + } + return result; +} + +/* + (nth number list) + Returns the nth item in list, counting from zero. +*/ +object* fn_nth(object* args, object* env) { + (void)env; + int n = checkinteger(first(args)); + if (n < 0) error(indexnegative, first(args)); + object* list = second(args); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + if (n == 0) return car(list); + list = cdr(list); + n--; + } + return nil; +} + +/* + (aref array index [index*]) + Returns an element from the specified array. +*/ +object* fn_aref(object* args, object* env) { + (void)env; + int bit; + object* array = first(args); + if (!arrayp(array)) error("first argument is not an array", array); + object* loc = *getarray(array, cdr(args), 0, &bit); + if (bit == -1) return loc; + else return number((loc->integer) >> bit & 1); +} + +/* + (assoc key list [:test function]) + Looks up a key in an association list of (key . value) pairs, using eq or the specified test function, + and returns the matching pair, or nil if no pair is found. +*/ +object* fn_assoc(object* args, object* env) { + (void)env; + object* key = first(args); + object* list = second(args); + object* test = testargument(cddr(args)); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + object* pair = first(list); + if (!listp(pair)) error("element is not a list", pair); + if (pair != NULL && apply(test, cons(key, cons(car(pair), NULL)), env) != NULL) return pair; + list = cdr(list); + } + return nil; +} + +/* + (member item list [:test function]) + Searches for an item in a list, using eq or the specified test function, and returns the list starting + from the first occurrence of the item, or nil if it is not found. +*/ +object* fn_member(object* args, object* env) { + (void)env; + object* item = first(args); + object* list = second(args); + object* test = testargument(cddr(args)); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + if (apply(test, cons(item, cons(car(list), NULL)), env) != NULL) return list; + list = cdr(list); + } + return nil; +} + +/* + (apply function list) + Returns the result of evaluating function, with the list of arguments specified by the second parameter. +*/ +object* fn_apply(object* args, object* env) { + object* previous = NULL; + object* last = args; + while (cdr(last) != NULL) { + previous = last; + last = cdr(last); + } + object* arg = car(last); + if (!listp(arg)) error(notalist, arg); + cdr(previous) = arg; + return apply(first(args), cdr(args), env); +} + +/* + (funcall function argument*) + Evaluates function with the specified arguments. +*/ +object* fn_funcall(object* args, object* env) { + return apply(first(args), cdr(args), env); +} + +/* + (append list*) + Joins its arguments, which should be lists, into a single list. +*/ +object* fn_append(object* args, object* env) { + (void)env; + object* head = NULL; + object* tail; + while (args != NULL) { + object* list = first(args); + if (!listp(list)) error(notalist, list); + while (consp(list)) { + object* obj = cons(car(list), cdr(list)); + if (head == NULL) head = obj; + else cdr(tail) = obj; + tail = obj; + list = cdr(list); + if (cdr(args) != NULL && improperp(list)) error(notproper, first(args)); + } + args = cdr(args); + } + return head; +} + +/* + (mapc function list1 [list]*) + Applies the function to each element in one or more lists, ignoring the results. + It returns the first list argument. +*/ +object* fn_mapc(object* args, object* env) { + return mapcl(args, env, false); +} + +/* + (mapl function list1 [list]*) + Applies the function to one or more lists and then successive cdrs of those lists, + ignoring the results. It returns the first list argument. +*/ +object* fn_mapl(object* args, object* env) { + return mapcl(args, env, true); +} + +/* + (mapcar function list1 [list]*) + Applies the function to each element in one or more lists, and returns the resulting list. +*/ +object* fn_mapcar(object* args, object* env) { + return mapcarcan(args, env, mapcarfun, false); +} + +/* + (mapcan function list1 [list]*) + Applies the function to each element in one or more lists. The results should be lists, + and these are destructively nconc'ed together to give the value returned. +*/ +object* fn_mapcan(object* args, object* env) { + return mapcarcan(args, env, mapcanfun, false); +} + +/* + (maplist function list1 [list]*) + Applies the function to one or more lists and then successive cdrs of those lists, + and returns the resulting list. +*/ +object* fn_maplist(object* args, object* env) { + return mapcarcan(args, env, mapcarfun, true); +} + +/* + (mapcon function list1 [list]*) + Applies the function to one or more lists and then successive cdrs of those lists, + and these are destructively concatenated together to give the value returned. +*/ +object* fn_mapcon(object* args, object* env) { + return mapcarcan(args, env, mapcanfun, true); +} + +// Arithmetic functions + +/* + (+ number*) + Adds its arguments together. + If each argument is an integer, and the running total doesn't overflow, the result is an integer, + otherwise a floating-point number. +*/ +object* fn_add(object* args, object* env) { + (void)env; + int result = 0; + while (args != NULL) { + object* arg = car(args); + if (floatp(arg)) return add_floats(args, (float)result); + else if (integerp(arg)) { + int val = arg->integer; + if (val < 1) { + if (INT_MIN - val > result) return add_floats(args, (float)result); + } else { + if (INT_MAX - val < result) return add_floats(args, (float)result); + } + result = result + val; + } else error(notanumber, arg); + args = cdr(args); + } + return number(result); +} + +/* + (- number*) + If there is one argument, negates the argument. + If there are two or more arguments, subtracts the second and subsequent arguments from the first argument. + If each argument is an integer, and the running total doesn't overflow, returns the result as an integer, + otherwise a floating-point number. +*/ +object* fn_subtract(object* args, object* env) { + (void)env; + object* arg = car(args); + args = cdr(args); + if (args == NULL) return negate(arg); + else if (floatp(arg)) return subtract_floats(args, arg->single_float); + else if (integerp(arg)) { + int result = arg->integer; + while (args != NULL) { + arg = car(args); + if (floatp(arg)) return subtract_floats(args, result); + else if (integerp(arg)) { + int val = (car(args))->integer; + if (val < 1) { + if (INT_MAX + val < result) return subtract_floats(args, result); + } else { + if (INT_MIN + val > result) return subtract_floats(args, result); + } + result = result - val; + } else error(notanumber, arg); + args = cdr(args); + } + return number(result); + } else error(notanumber, arg); + return nil; +} + +/* + (* number*) + Multiplies its arguments together. + If each argument is an integer, and the running total doesn't overflow, the result is an integer, + otherwise it's a floating-point number. +*/ +object* fn_multiply(object* args, object* env) { + (void)env; + int result = 1; + while (args != NULL) { + object* arg = car(args); + if (floatp(arg)) return multiply_floats(args, result); + else if (integerp(arg)) { + int64_t val = result * (int64_t)(arg->integer); + if ((val > INT_MAX) || (val < INT_MIN)) return multiply_floats(args, result); + result = val; + } else error(notanumber, arg); + args = cdr(args); + } + return number(result); +} + +/* + (/ number*) + Divides the first argument by the second and subsequent arguments. + If each argument is an integer, and each division produces an exact result, the result is an integer; + otherwise it's a floating-point number. +*/ +object* fn_divide(object* args, object* env) { + (void)env; + object* arg = first(args); + args = cdr(args); + // One argument + if (args == NULL) { + if (floatp(arg)) { + float f = arg->single_float; + if (f == 0.0) error2("division by zero"); + return makefloat(1.0 / f); + } else if (integerp(arg)) { + int i = arg->integer; + if (i == 0) error2("division by zero"); + else if (i == 1) return number(1); + else return makefloat(1.0 / i); + } else error(notanumber, arg); + } + // Multiple arguments + if (floatp(arg)) return divide_floats(args, arg->single_float); + else if (integerp(arg)) { + int result = arg->integer; + while (args != NULL) { + arg = car(args); + if (floatp(arg)) { + return divide_floats(args, result); + } else if (integerp(arg)) { + int i = arg->integer; + if (i == 0) error2("division by zero"); + if ((result % i) != 0) return divide_floats(args, result); + if ((result == INT_MIN) && (i == -1)) return divide_floats(args, result); + result = result / i; + args = cdr(args); + } else error(notanumber, arg); + } + return number(result); + } else error(notanumber, arg); + return nil; +} + +/* + (mod number number) + Returns its first argument modulo the second argument. + If both arguments are integers the result is an integer; otherwise it's a floating-point number. +*/ +object* fn_mod(object* args, object* env) { + (void)env; + object* arg1 = first(args); + object* arg2 = second(args); + if (integerp(arg1) && integerp(arg2)) { + int divisor = arg2->integer; + if (divisor == 0) error2("division by zero"); + int dividend = arg1->integer; + int remainder = dividend % divisor; + if ((dividend < 0) != (divisor < 0)) remainder = remainder + divisor; + return number(remainder); + } else { + float fdivisor = checkintfloat(arg2); + if (fdivisor == 0.0) error2("division by zero"); + float fdividend = checkintfloat(arg1); + float fremainder = fmod(fdividend, fdivisor); + if ((fdividend < 0) != (fdivisor < 0)) fremainder = fremainder + fdivisor; + return makefloat(fremainder); + } +} + +/* + (1+ number) + Adds one to its argument and returns it. + If the argument is an integer the result is an integer if possible; + otherwise it's a floating-point number. +*/ +object* fn_oneplus(object* args, object* env) { + (void)env; + object* arg = first(args); + if (floatp(arg)) return makefloat((arg->single_float) + 1.0); + else if (integerp(arg)) { + int result = arg->integer; + if (result == INT_MAX) return makefloat((arg->integer) + 1.0); + else return number(result + 1); + } else error(notanumber, arg); + return nil; +} + +/* + (1- number) + Subtracts one from its argument and returns it. + If the argument is an integer the result is an integer if possible; + otherwise it's a floating-point number. +*/ +object* fn_oneminus(object* args, object* env) { + (void)env; + object* arg = first(args); + if (floatp(arg)) return makefloat((arg->single_float) - 1.0); + else if (integerp(arg)) { + int result = arg->integer; + if (result == INT_MIN) return makefloat((arg->integer) - 1.0); + else return number(result - 1); + } else error(notanumber, arg); + return nil; +} + +/* + (abs number) + Returns the absolute, positive value of its argument. + If the argument is an integer the result will be returned as an integer if possible, + otherwise a floating-point number. +*/ +object* fn_abs(object* args, object* env) { + (void)env; + object* arg = first(args); + if (floatp(arg)) return makefloat(abs(arg->single_float)); + else if (integerp(arg)) { + int result = arg->integer; + if (result == INT_MIN) return makefloat(abs((float)result)); + else return number(abs(result)); + } else error(notanumber, arg); + return nil; +} + +/* + (random number) + If number is an integer returns a random number between 0 and one less than its argument. + Otherwise returns a floating-point number between zero and number. +*/ +object* fn_random(object* args, object* env) { + (void)env; + object* arg = first(args); + if (integerp(arg)) return number(random(arg->integer)); + else if (floatp(arg)) return makefloat((float)rand() / (float)(RAND_MAX / (arg->single_float))); + else error(notanumber, arg); + return nil; +} + +/* + (max number*) + Returns the maximum of one or more arguments. +*/ +object* fn_maxfn(object* args, object* env) { + (void)env; + object* result = first(args); + args = cdr(args); + while (args != NULL) { + object* arg = car(args); + if (integerp(result) && integerp(arg)) { + if ((arg->integer) > (result->integer)) result = arg; + } else if ((checkintfloat(arg) > checkintfloat(result))) result = arg; + args = cdr(args); + } + return result; +} + +/* + (min number*) + Returns the minimum of one or more arguments. +*/ +object* fn_minfn(object* args, object* env) { + (void)env; + object* result = first(args); + args = cdr(args); + while (args != NULL) { + object* arg = car(args); + if (integerp(result) && integerp(arg)) { + if ((arg->integer) < (result->integer)) result = arg; + } else if ((checkintfloat(arg) < checkintfloat(result))) result = arg; + args = cdr(args); + } + return result; +} + +// Arithmetic comparisons + +/* + (/= number*) + Returns t if none of the arguments are equal, or nil if two or more arguments are equal. +*/ +object* fn_noteq(object* args, object* env) { + (void)env; + while (args != NULL) { + object* nargs = args; + object* arg1 = first(nargs); + nargs = cdr(nargs); + while (nargs != NULL) { + object* arg2 = first(nargs); + if (integerp(arg1) && integerp(arg2)) { + if ((arg1->integer) == (arg2->integer)) return nil; + } else if ((checkintfloat(arg1) == checkintfloat(arg2))) return nil; + nargs = cdr(nargs); + } + args = cdr(args); + } + return tee; +} + +/* + (= number*) + Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise. +*/ +object* fn_numeq(object* args, object* env) { + (void)env; + return compare(args, false, false, true); +} + +/* + (< number*) + Returns t if each argument is less than the next argument, and nil otherwise. +*/ +object* fn_less(object* args, object* env) { + (void)env; + return compare(args, true, false, false); +} + +/* + (<= number*) + Returns t if each argument is less than or equal to the next argument, and nil otherwise. +*/ +object* fn_lesseq(object* args, object* env) { + (void)env; + return compare(args, true, false, true); +} + +/* + (> number*) + Returns t if each argument is greater than the next argument, and nil otherwise. +*/ +object* fn_greater(object* args, object* env) { + (void)env; + return compare(args, false, true, false); +} + +/* + (>= number*) + Returns t if each argument is greater than or equal to the next argument, and nil otherwise. +*/ +object* fn_greatereq(object* args, object* env) { + (void)env; + return compare(args, false, true, true); +} + +/* + (plusp number) + Returns t if the argument is greater than zero, or nil otherwise. +*/ +object* fn_plusp(object* args, object* env) { + (void)env; + object* arg = first(args); + if (floatp(arg)) return ((arg->single_float) > 0.0) ? tee : nil; + else if (integerp(arg)) return ((arg->integer) > 0) ? tee : nil; + else error(notanumber, arg); + return nil; +} + +/* + (minusp number) + Returns t if the argument is less than zero, or nil otherwise. +*/ +object* fn_minusp(object* args, object* env) { + (void)env; + object* arg = first(args); + if (floatp(arg)) return ((arg->single_float) < 0.0) ? tee : nil; + else if (integerp(arg)) return ((arg->integer) < 0) ? tee : nil; + else error(notanumber, arg); + return nil; +} + +/* + (zerop number) + Returns t if the argument is zero. +*/ +object* fn_zerop(object* args, object* env) { + (void)env; + object* arg = first(args); + if (floatp(arg)) return ((arg->single_float) == 0.0) ? tee : nil; + else if (integerp(arg)) return ((arg->integer) == 0) ? tee : nil; + else error(notanumber, arg); + return nil; +} + +/* + (oddp number) + Returns t if the integer argument is odd. +*/ +object* fn_oddp(object* args, object* env) { + (void)env; + int arg = checkinteger(first(args)); + return ((arg & 1) == 1) ? tee : nil; +} + +/* + (evenp number) + Returns t if the integer argument is even. +*/ +object* fn_evenp(object* args, object* env) { + (void)env; + int arg = checkinteger(first(args)); + return ((arg & 1) == 0) ? tee : nil; +} + +// Number functions + +/* + (integerp number) + Returns t if the argument is an integer. +*/ +object* fn_integerp(object* args, object* env) { + (void)env; + return integerp(first(args)) ? tee : nil; +} + +/* + (numberp number) + Returns t if the argument is a number. +*/ +object* fn_numberp(object* args, object* env) { + (void)env; + object* arg = first(args); + return (integerp(arg) || floatp(arg)) ? tee : nil; +} + +// Floating-point functions + +/* + (float number) + Returns its argument converted to a floating-point number. +*/ +object* fn_floatfn(object* args, object* env) { + (void)env; + object* arg = first(args); + return (floatp(arg)) ? arg : makefloat((float)(arg->integer)); +} + +/* + (floatp number) + Returns t if the argument is a floating-point number. +*/ +object* fn_floatp(object* args, object* env) { + (void)env; + return floatp(first(args)) ? tee : nil; +} + +/* + (sin number) + Returns sin(number). +*/ +object* fn_sin(object* args, object* env) { + (void)env; + return makefloat(sin(checkintfloat(first(args)))); +} + +/* + (cos number) + Returns cos(number). +*/ +object* fn_cos(object* args, object* env) { + (void)env; + return makefloat(cos(checkintfloat(first(args)))); +} + +/* + (tan number) + Returns tan(number). +*/ +object* fn_tan(object* args, object* env) { + (void)env; + return makefloat(tan(checkintfloat(first(args)))); +} + +/* + (asin number) + Returns asin(number). +*/ +object* fn_asin(object* args, object* env) { + (void)env; + return makefloat(asin(checkintfloat(first(args)))); +} + +/* + (acos number) + Returns acos(number). +*/ +object* fn_acos(object* args, object* env) { + (void)env; + return makefloat(acos(checkintfloat(first(args)))); +} + +/* + (atan number1 [number2]) + Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1. +*/ +object* fn_atan(object* args, object* env) { + (void)env; + object* arg = first(args); + float div = 1.0; + args = cdr(args); + if (args != NULL) div = checkintfloat(first(args)); + return makefloat(atan2(checkintfloat(arg), div)); +} + +/* + (sinh number) + Returns sinh(number). +*/ +object* fn_sinh(object* args, object* env) { + (void)env; + return makefloat(sinh(checkintfloat(first(args)))); +} + +/* + (cosh number) + Returns cosh(number). +*/ +object* fn_cosh(object* args, object* env) { + (void)env; + return makefloat(cosh(checkintfloat(first(args)))); +} + +/* + (tanh number) + Returns tanh(number). +*/ +object* fn_tanh(object* args, object* env) { + (void)env; + return makefloat(tanh(checkintfloat(first(args)))); +} + +/* + (exp number) + Returns exp(number). +*/ +object* fn_exp(object* args, object* env) { + (void)env; + return makefloat(exp(checkintfloat(first(args)))); +} + +/* + (sqrt number) + Returns sqrt(number). +*/ +object* fn_sqrt(object* args, object* env) { + (void)env; + return makefloat(sqrt(checkintfloat(first(args)))); +} + +/* + (log number [base]) + Returns the logarithm of number to the specified base. If base is omitted it defaults to e. +*/ +object* fn_log(object* args, object* env) { + (void)env; + object* arg = first(args); + float fresult = log(checkintfloat(arg)); + args = cdr(args); + if (args == NULL) return makefloat(fresult); + else return makefloat(fresult / log(checkintfloat(first(args)))); +} + +/* + (expt number power) + Returns number raised to the specified power. + Returns the result as an integer if the arguments are integers and the result will be within range, + otherwise a floating-point number. +*/ +object* fn_expt(object* args, object* env) { + (void)env; + object* arg1 = first(args); + object* arg2 = second(args); + float float1 = checkintfloat(arg1); + float value = log(abs(float1)) * checkintfloat(arg2); + if (integerp(arg1) && integerp(arg2) && ((arg2->integer) >= 0) && (abs(value) < 21.4875)) + return number(intpower(arg1->integer, arg2->integer)); + if (float1 < 0) { + if (integerp(arg2)) return makefloat((arg2->integer & 1) ? -exp(value) : exp(value)); + else error2("imaginary result"); + } + return makefloat(exp(value)); +} + +/* + (ceiling number [divisor]) + Returns ceil(number/divisor). If omitted, divisor is 1. +*/ +object* fn_ceiling(object* args, object* env) { + (void)env; + object* arg = first(args); + args = cdr(args); + if (args != NULL) return number(ceil(checkintfloat(arg) / checkintfloat(first(args)))); + else return number(ceil(checkintfloat(arg))); +} + +/* + (floor number [divisor]) + Returns floor(number/divisor). If omitted, divisor is 1. +*/ +object* fn_floor(object* args, object* env) { + (void)env; + object* arg = first(args); + args = cdr(args); + if (args != NULL) return number(floor(checkintfloat(arg) / checkintfloat(first(args)))); + else return number(floor(checkintfloat(arg))); +} + +/* + (truncate number [divisor]) + Returns the integer part of number/divisor. If divisor is omitted it defaults to 1. +*/ +object* fn_truncate(object* args, object* env) { + (void)env; + object* arg = first(args); + args = cdr(args); + if (args != NULL) return number((int)(checkintfloat(arg) / checkintfloat(first(args)))); + else return number((int)(checkintfloat(arg))); +} + +/* + (round number [divisor]) + Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1. +*/ +object* fn_round(object* args, object* env) { + (void)env; + object* arg = first(args); + args = cdr(args); + if (args != NULL) return number(round(checkintfloat(arg) / checkintfloat(first(args)))); + else return number(round(checkintfloat(arg))); +} + +// Characters + +/* + (char string n) + Returns the nth character in a string, counting from zero. +*/ +object* fn_char(object* args, object* env) { + (void)env; + object* arg = first(args); + if (!stringp(arg)) error(notastring, arg); + object* n = second(args); + char c = nthchar(arg, checkinteger(n)); + if (c == 0) error(indexrange, n); + return character(c); +} + +/* + (char-code character) + Returns the ASCII code for a character, as an integer. +*/ +object* fn_charcode(object* args, object* env) { + (void)env; + return number(checkchar(first(args))); +} + +/* + (code-char integer) + Returns the character for the specified ASCII code. +*/ +object* fn_codechar(object* args, object* env) { + (void)env; + return character(checkinteger(first(args))); +} + +/* + (characterp item) + Returns t if the argument is a character and nil otherwise. +*/ +object* fn_characterp(object* args, object* env) { + (void)env; + return characterp(first(args)) ? tee : nil; +} + +// Strings + +/* + (stringp item) + Returns t if the argument is a string and nil otherwise. +*/ +object* fn_stringp(object* args, object* env) { + (void)env; + return stringp(first(args)) ? tee : nil; +} + +/* + (string= string string) + Returns t if the two strings are the same, or nil otherwise. +*/ +object* fn_stringeq(object* args, object* env) { + (void)env; + int m = stringcompare(args, false, false, true); + return m == -1 ? nil : tee; +} + +/* + (string< string string) + Returns the index to the first mismatch if the first string is alphabetically less than the second string, + or nil otherwise. +*/ +object* fn_stringless(object* args, object* env) { + (void)env; + int m = stringcompare(args, true, false, false); + return m == -1 ? nil : number(m); +} + +/* + (string> string string) + Returns the index to the first mismatch if the first string is alphabetically greater than the second string, + or nil otherwise. +*/ +object* fn_stringgreater(object* args, object* env) { + (void)env; + int m = stringcompare(args, false, true, false); + return m == -1 ? nil : number(m); +} + +/* + (string/= string string) + Returns the index to the first mismatch if the two strings are not the same, or nil otherwise. +*/ +object* fn_stringnoteq(object* args, object* env) { + (void)env; + int m = stringcompare(args, true, true, false); + return m == -1 ? nil : number(m); +} + +/* + (string<= string string) + Returns the index to the first mismatch if the first string is alphabetically less than or equal to + the second string, or nil otherwise. +*/ +object* fn_stringlesseq(object* args, object* env) { + (void)env; + int m = stringcompare(args, true, false, true); + return m == -1 ? nil : number(m); +} + +/* + (string>= string string) + Returns the index to the first mismatch if the first string is alphabetically greater than or equal to + the second string, or nil otherwise. +*/ +object* fn_stringgreatereq(object* args, object* env) { + (void)env; + int m = stringcompare(args, false, true, true); + return m == -1 ? nil : number(m); +} + +/* + (sort list test) + Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list. +*/ +object* fn_sort(object* args, object* env) { + if (first(args) == NULL) return nil; + object* list = cons(nil, first(args)); + protect(list); + object* predicate = second(args); + object* compare = cons(NULL, cons(NULL, NULL)); + protect(compare); + object* ptr = cdr(list); + while (cdr(ptr) != NULL) { + object* go = list; + while (go != ptr) { + car(compare) = car(cdr(ptr)); + car(cdr(compare)) = car(cdr(go)); + if (apply(predicate, compare, env)) break; + go = cdr(go); + } + if (go != ptr) { + object* obj = cdr(ptr); + cdr(ptr) = cdr(obj); + cdr(obj) = cdr(go); + cdr(go) = obj; + } else ptr = cdr(ptr); + } + unprotect(); + unprotect(); + return cdr(list); +} + +/* + (string item) + Converts its argument to a string. +*/ +object* fn_stringfn(object* args, object* env) { + return fn_princtostring(args, env); +} + +/* + (concatenate 'string string*) + Joins together the strings given in the second and subsequent arguments, and returns a single string. +*/ +object* fn_concatenate(object* args, object* env) { + (void)env; + object* arg = first(args); + if (builtin(arg->name) != STRINGFN) error2("only supports strings"); + args = cdr(args); + object* result = newstring(); + object* tail = result; + while (args != NULL) { + object* obj = checkstring(first(args)); + obj = cdr(obj); + while (obj != NULL) { + int quad = obj->chars; + while (quad != 0) { + char ch = quad >> ((sizeof(int) - 1) * 8) & 0xFF; + buildstring(ch, &tail); + quad = quad << 8; + } + obj = car(obj); + } + args = cdr(args); + } + return result; +} + +/* + (subseq seq start [end]) + Returns a subsequence of a list or string from item start to item end-1. +*/ +object* fn_subseq(object* args, object* env) { + (void)env; + object* arg = first(args); + int start = checkinteger(second(args)), end; + if (start < 0) error(indexnegative, second(args)); + args = cddr(args); + if (listp(arg)) { + int length = listlength(arg); + if (args != NULL) end = checkinteger(car(args)); + else end = length; + if (start > end || end > length) error2(indexrange); + object* result = cons(NULL, NULL); + object* ptr = result; + for (int x = 0; x < end; x++) { + if (x >= start) { + cdr(ptr) = cons(car(arg), NULL); + ptr = cdr(ptr); + } + arg = cdr(arg); + } + return cdr(result); + } else if (stringp(arg)) { + int length = stringlength(arg); + if (args != NULL) end = checkinteger(car(args)); + else end = length; + if (start > end || end > length) error2(indexrange); + object* result = newstring(); + object* tail = result; + for (int i = start; i < end; i++) { + char ch = nthchar(arg, i); + buildstring(ch, &tail); + } + return result; + } else error2("argument is not a list or string"); + return nil; +} + +/* + (search pattern target [:test function]) + Returns the index of the first occurrence of pattern in target, or nil if it's not found. + The target can be a list or string. If it's a list a test function can be specified; default eq. +*/ +object* fn_search(object* args, object* env) { + (void)env; + object* pattern = first(args); + object* target = second(args); + if (pattern == NULL) return number(0); + else if (target == NULL) return nil; + else if (listp(pattern) && listp(target)) { + object* test = testargument(cddr(args)); + int l = listlength(target); + int m = listlength(pattern); + for (int i = 0; i <= l - m; i++) { + object* target1 = target; + while (pattern != NULL && apply(test, cons(car(target1), cons(car(pattern), NULL)), env) != NULL) { + pattern = cdr(pattern); + target1 = cdr(target1); + } + if (pattern == NULL) return number(i); + pattern = first(args); + target = cdr(target); + } + return nil; + } else if (stringp(pattern) && stringp(target)) { + if (cddr(args) != NULL) error2("use of :test argument not supported for strings"); + int l = stringlength(target); + int m = stringlength(pattern); + for (int i = 0; i <= l - m; i++) { + int j = 0; + while (j < m && nthchar(target, i + j) == nthchar(pattern, j)) j++; + if (j == m) return number(i); + } + return nil; + } else error2("arguments are not both lists or strings"); + return nil; +} + +/* + (read-from-string string) + Reads an atom or list from the specified string and returns it. +*/ +object* fn_readfromstring(object* args, object* env) { + (void)env; + object* arg = checkstring(first(args)); + GlobalString = arg; + GlobalStringIndex = 0; + object* val = read(gstr); + LastChar = 0; + return val; +} + +/* + (princ-to-string item) + Prints its argument to a string, and returns the string. + Characters and strings are printed without quotation marks or escape characters. +*/ +object* fn_princtostring(object* args, object* env) { + (void)env; + return princtostring(first(args)); +} + +/* + (prin1-to-string item [stream]) + Prints its argument to a string, and returns the string. + Characters and strings are printed with quotation marks and escape characters, + in a format that will be suitable for read-from-string. +*/ +object* fn_prin1tostring(object* args, object* env) { + (void)env; + object* arg = first(args); + object* obj = startstring(); + printobject(arg, pstr); + return obj; +} + +// Bitwise operators + +/* + (logand [value*]) + Returns the bitwise & of the values. +*/ +object* fn_logand(object* args, object* env) { + (void)env; + int result = -1; + while (args != NULL) { + result = result & checkinteger(first(args)); + args = cdr(args); + } + return number(result); +} + +/* + (logior [value*]) + Returns the bitwise | of the values. +*/ +object* fn_logior(object* args, object* env) { + (void)env; + int result = 0; + while (args != NULL) { + result = result | checkinteger(first(args)); + args = cdr(args); + } + return number(result); +} + +/* + (logxor [value*]) + Returns the bitwise ^ of the values. +*/ +object* fn_logxor(object* args, object* env) { + (void)env; + int result = 0; + while (args != NULL) { + result = result ^ checkinteger(first(args)); + args = cdr(args); + } + return number(result); +} + +/* + (lognot value) + Returns the bitwise logical NOT of the value. +*/ +object* fn_lognot(object* args, object* env) { + (void)env; + int result = checkinteger(car(args)); + return number(~result); +} + +/* + (ash value shift) + Returns the result of bitwise shifting value by shift bits. If shift is positive, value is shifted to the left. +*/ +object* fn_ash(object* args, object* env) { + (void)env; + int value = checkinteger(first(args)); + int count = checkinteger(second(args)); + if (count >= 0) return number(value << count); + else return number(value >> abs(count)); +} + +/* + (logbitp bit value) + Returns t if bit number bit in value is a '1', and nil if it is a '0'. +*/ +object* fn_logbitp(object* args, object* env) { + (void)env; + int index = checkinteger(first(args)); + int value = checkinteger(second(args)); + return (bitRead(value, index) == 1) ? tee : nil; +} + +// System functions + +/* + (eval form*) + Evaluates its argument an extra time. +*/ +object* fn_eval(object* args, object* env) { + return eval(first(args), env); +} + +/* + (globals) + Returns a list of global variables. +*/ +object* fn_globals(object* args, object* env) { + (void)args, (void)env; + object* result = cons(NULL, NULL); + object* ptr = result; + object* arg = GlobalEnv; + while (arg != NULL) { + cdr(ptr) = cons(car(car(arg)), NULL); + ptr = cdr(ptr); + arg = cdr(arg); + } + return cdr(result); +} + +/* + (locals) + Returns an association list of local variables and their values. +*/ +object* fn_locals(object* args, object* env) { + (void)args; + return env; +} + +/* + (makunbound symbol) + Removes the value of the symbol from GlobalEnv and returns the symbol. +*/ +object* fn_makunbound(object* args, object* env) { + (void)env; + object* var = first(args); + if (!symbolp(var)) error(notasymbol, var); + delassoc(var, &GlobalEnv); + return var; +} + +/* + (break) + Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL. +*/ +object* fn_break(object* args, object* env) { + (void)args; + pfstring("\nBreak!\n", pserial); + BreakLevel++; + repl(env); + BreakLevel--; + return nil; +} + +/* + (read [stream]) + Reads an atom or list from the serial input and returns it. + If stream is specified the item is read from the specified stream. +*/ +object* fn_read(object* args, object* env) { + (void)env; + gfun_t gfun = gstreamfun(args); + return read(gfun); +} + +/* + (prin1 item [stream]) + Prints its argument, and returns its value. + Strings are printed with quotation marks and escape characters. +*/ +object* fn_prin1(object* args, object* env) { + (void)env; + object* obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + printobject(obj, pfun); + return obj; +} + +/* + (print item [stream]) + Prints its argument with quotation marks and escape characters, on a new line, and followed by a space. + If stream is specified the argument is printed to the specified stream. +*/ +object* fn_print(object* args, object* env) { + (void)env; + object* obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + pln(pfun); + printobject(obj, pfun); + pfun(' '); + return obj; +} + +/* + (princ item [stream]) + Prints its argument, and returns its value. + Characters and strings are printed without quotation marks or escape characters. +*/ +object* fn_princ(object* args, object* env) { + (void)env; + object* obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + prin1object(obj, pfun); + return obj; +} + +/* + (terpri [stream]) + Prints a new line, and returns nil. + If stream is specified the new line is written to the specified stream. +*/ +object* fn_terpri(object* args, object* env) { + (void)env; + pfun_t pfun = pstreamfun(args); + pln(pfun); + return nil; +} + +/* + (read-byte stream) + Reads a byte from a stream and returns it. +*/ +object* fn_readbyte(object* args, object* env) { + (void)env; + gfun_t gfun = gstreamfun(args); + int c = gfun(); + return (c == -1) ? nil : number(c); +} + +/* + (read-line [stream]) + Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline. + If stream is specified the line is read from the specified stream. +*/ +object* fn_readline(object* args, object* env) { + (void)env; + gfun_t gfun = gstreamfun(args); + return readstring('\n', false, gfun); +} + +/* + (write-byte number [stream]) + Writes a byte to a stream. +*/ +object* fn_writebyte(object* args, object* env) { + (void)env; + int value = checkinteger(first(args)); + pfun_t pfun = pstreamfun(cdr(args)); + (pfun)(value); + return nil; +} + +/* + (write-string string [stream]) + Writes a string. If stream is specified the string is written to the stream. +*/ +object* fn_writestring(object* args, object* env) { + (void)env; + object* obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printstring(obj, pfun); + Flags = temp; + return nil; +} + +/* + (write-line string [stream]) + Writes a string terminated by a newline character. If stream is specified the string is written to the stream. +*/ +object* fn_writeline(object* args, object* env) { + (void)env; + object* obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printstring(obj, pfun); + pln(pfun); + Flags = temp; + return nil; +} + +/* + (restart-i2c stream [read-p]) + Restarts an i2c-stream. + If read-p is nil or omitted the stream is written to. + If read-p is an integer it specifies the number of bytes to be read from the stream. +*/ +object* fn_restarti2c(object* args, object* env) { + (void)env; + int stream = isstream(first(args)); + args = cdr(args); + int read = 0; // Write + I2Ccount = 0; + if (args != NULL) { + object* rw = first(args); + if (integerp(rw)) I2Ccount = rw->integer; + read = (rw != NULL); + } + int address = stream & 0xFF; + if (stream >> 8 != I2CSTREAM) error2("not an i2c stream"); + TwoWire* port; + if (address < 128) port = &Wire; +#if defined(ULISP_I2C1) + else port = &Wire1; +#endif + return I2Crestart(port, address & 0x7F, read) ? tee : nil; +} + +/* + (gc) + Forces a garbage collection and prints the number of objects collected, and the time taken. +*/ +object* fn_gc(object* obj, object* env) { + int initial = Freespace; + unsigned long start = micros(); + gc(obj, env); + unsigned long elapsed = micros() - start; + pfstring("Space: ", pserial); + pint(Freespace - initial, pserial); + pfstring(" bytes, Time: ", pserial); + pint(elapsed, pserial); + pfstring(" us\n", pserial); + return nil; +} + +/* + (room) + Returns the number of free Lisp cells remaining. +*/ +object* fn_room(object* args, object* env) { + (void)args, (void)env; + return number(Freespace); +} + +/* + (cls) + Prints a clear-screen character. +*/ +object* fn_cls(object* args, object* env) { + (void)args, (void)env; + pserial(12); + return nil; +} + +// Arduino procedures + +/* + (pinmode pin mode) + Sets the input/output mode of an Arduino pin number, and returns nil. + The mode parameter can be an integer, a keyword, or t or nil. +*/ +object* fn_pinmode(object* args, object* env) { + (void)env; + int pin; + object* arg = first(args); + if (builtin_keywordp(arg)) pin = checkkeyword(arg); + else pin = checkinteger(first(args)); + int pm = INPUT; + arg = second(args); + if (builtin_keywordp(arg)) pm = checkkeyword(arg); + else if (integerp(arg)) { + int mode = arg->integer; + if (mode == 1) pm = OUTPUT; + else if (mode == 2) pm = INPUT_PULLUP; +#if defined(INPUT_PULLDOWN) + else if (mode == 4) pm = INPUT_PULLDOWN; +#endif + } else if (arg != nil) pm = OUTPUT; + pinMode(pin, pm); + return nil; +} + +/* + (digitalread pin) + Reads the state of the specified Arduino pin number and returns t (high) or nil (low). +*/ +object* fn_digitalread(object* args, object* env) { + (void)env; + int pin; + object* arg = first(args); + if (builtin_keywordp(arg)) pin = checkkeyword(arg); + else pin = checkinteger(arg); + if (digitalRead(pin) != 0) return tee; + else return nil; +} + +/* + (digitalwrite pin state) + Sets the state of the specified Arduino pin number. +*/ +object* fn_digitalwrite(object* args, object* env) { + (void)env; + int pin; + object* arg = first(args); + if (builtin_keywordp(arg)) pin = checkkeyword(arg); + else pin = checkinteger(arg); + arg = second(args); + int mode; + if (builtin_keywordp(arg)) mode = checkkeyword(arg); + else if (integerp(arg)) mode = arg->integer ? HIGH : LOW; + else mode = (arg != nil) ? HIGH : LOW; + digitalWrite(pin, mode); + return arg; +} + +/* + (analogread pin) + Reads the specified Arduino analogue pin number and returns the value. +*/ +object* fn_analogread(object* args, object* env) { + (void)env; + int pin; + object* arg = first(args); + if (builtin_keywordp(arg)) pin = checkkeyword(arg); + else { + pin = checkinteger(arg); + checkanalogread(pin); + } + return number(analogRead(pin)); +} + +/* + (analogreadresolution bits) + Specifies the resolution for the analogue inputs on platforms that support it. + The default resolution on all platforms is 10 bits. +*/ +object* fn_analogreadresolution(object* args, object* env) { + (void)env; + object* arg = first(args); + analogReadResolution(checkinteger(arg)); + return arg; +} + +/* + (analogwrite pin value) + Writes the value to the specified Arduino pin number. +*/ +object* fn_analogwrite(object* args, object* env) { + (void)env; + int pin; + object* arg = first(args); + if (builtin_keywordp(arg)) pin = checkkeyword(arg); + else pin = checkinteger(arg); + checkanalogwrite(pin); + object* value = second(args); +#ifdef toneimplemented + analogWrite +#else + dacWrite +#endif + (pin, checkinteger(value)); + return value; +} + +/* + (delay number) + Delays for a specified number of milliseconds. +*/ +object* fn_delay(object* args, object* env) { + (void)env; + object* arg1 = first(args); + unsigned long start = millis(); + unsigned long total = checkinteger(arg1); + do testescape(); + while (millis() - start < total); + return arg1; +} + +/* + (millis) + Returns the time in milliseconds that uLisp has been running. +*/ +object* fn_millis(object* args, object* env) { + (void)args, (void)env; + return number(millis()); +} + +/* + (sleep secs) + Puts the processor into a low-power sleep mode for secs. + Only supported on some platforms. On other platforms it does delay(1000*secs). +*/ +object* fn_sleep(object* args, object* env) { + (void)env; + object* arg1 = first(args); + doze(checkinteger(arg1)); + return arg1; +} + +/* + (note [pin] [note] [octave]) + Generates a square wave on pin. + The argument note represents the note in the well-tempered scale, from 0 to 11, + where 0 represents C, 1 represents C#, and so on. + The argument octave can be from 3 to 6. If omitted it defaults to 0. + When called with no arguments, turns off the PWM on the last-used pin. +*/ +object* fn_note(object* args, object* env) { + (void)env; + static int pin = 255; + if (args != NULL) { + pin = checkinteger(first(args)); + int note = 48, octave = 0; + if (cdr(args) != NULL) { + note = checkinteger(second(args)); + if (cddr(args) != NULL) octave = checkinteger(third(args)); + } + playnote(pin, note, octave); + } else nonote(pin); + return nil; +} + +/* + (register address [value]) + Reads or writes the value of a peripheral register. + If value is not specified the function returns the value of the register at address. + If value is specified the value is written to the register at address and the function returns value. +*/ +object* fn_register(object* args, object* env) { + (void)env; + object* arg = first(args); + int addr; + if (builtin_keywordp(arg)) addr = checkkeyword(arg); + else addr = checkinteger(first(args)); + if (cdr(args) == NULL) return number(*(uint32_t*)addr); + (*(uint32_t*)addr) = checkinteger(second(args)); + return second(args); +} + +// Tree Editor + +/* + (edit 'function) + Calls the Lisp tree editor to allow you to edit a function definition. +*/ +object* fn_edit(object* args, object* env) { + object* fun = first(args); + object* pair = findvalue(fun, env); + clrflag(EXITEDITOR); + object* arg = edit(eval(fun, env)); + cdr(pair) = arg; + return arg; +} + +// Pretty printer + +/* + (pprint item [str]) + Prints its argument, using the pretty printer, to display it formatted in a structured way. + If str is specified it prints to the specified stream. It returns no value. +*/ +object* fn_pprint(object* args, object* env) { + (void)env; + object* obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); +#if defined(gfxsupport) + if (pfun == gfxwrite) ppwidth = GFXPPWIDTH; +#endif + pln(pfun); + superprint(obj, 0, pfun); + ppwidth = PPWIDTH; + return bsymbol(NOTHING); +} + +/* + (pprintall [str]) + Pretty-prints the definition of every function and variable defined in the uLisp workspace. + If str is specified it prints to the specified stream. It returns no value. +*/ +object* fn_pprintall(object* args, object* env) { + (void)env; + pfun_t pfun = pstreamfun(args); +#if defined(gfxsupport) + if (pfun == gfxwrite) ppwidth = GFXPPWIDTH; +#endif + object* globals = GlobalEnv; + while (globals != NULL) { + object* pair = first(globals); + object* var = car(pair); + object* val = cdr(pair); + pln(pfun); + if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) { + superprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun); + } else { + superprint(cons(bsymbol(DEFVAR), cons(var, cons(quoteit(QUOTE, val), NULL))), 0, pfun); + } + pln(pfun); + testescape(); + globals = cdr(globals); + } + ppwidth = PPWIDTH; + return bsymbol(NOTHING); +} + +// Format + +/* + (format output controlstring [arguments]*) + Outputs its arguments formatted according to the format directives in controlstring. +*/ +object* fn_format(object* args, object* env) { + (void)env; + pfun_t pfun = pserial; + object* output = first(args); + object* obj; + if (output == nil) { + obj = startstring(); + pfun = pstr; + } else if (output != tee) pfun = pstreamfun(args); + object* formatstr = checkstring(second(args)); + object* save = NULL; + args = cddr(args); + int len = stringlength(formatstr); + uint8_t n = 0, width = 0, w, bra = 0; + char pad = ' '; + bool tilde = false, mute = false, comma = false, quote = false; + while (n < len) { + char ch = nthchar(formatstr, n); + char ch2 = ch & ~0x20; // force to upper case + if (tilde) { + if (ch == '}') { + if (save == NULL) formaterr(formatstr, "no matching ~{", n); + if (args == NULL) { + args = cdr(save); + save = NULL; + } else n = bra; + mute = false; + tilde = false; + } else if (!mute) { + if (comma && quote) { + pad = ch; + comma = false, quote = false; + } else if (ch == '\'') { + if (comma) quote = true; + else formaterr(formatstr, "quote not valid", n); + } else if (ch == '~') { + pfun('~'); + tilde = false; + } else if (ch >= '0' && ch <= '9') width = width * 10 + ch - '0'; + else if (ch == ',') comma = true; + else if (ch == '%') { + pln(pfun); + tilde = false; + } else if (ch == '&') { + pfl(pfun); + tilde = false; + } else if (ch == '^') { + if (save != NULL && args == NULL) mute = true; + tilde = false; + } else if (ch == '{') { + if (save != NULL) formaterr(formatstr, "can't nest ~{", n); + if (args == NULL) formaterr(formatstr, noargument, n); + if (!listp(first(args))) formaterr(formatstr, notalist, n); + save = args; + args = first(args); + bra = n; + tilde = false; + if (args == NULL) mute = true; + } else if (ch2 == 'A' || ch2 == 'S' || ch2 == 'D' || ch2 == 'G' || ch2 == 'X' || ch2 == 'B') { + if (args == NULL) formaterr(formatstr, noargument, n); + object* arg = first(args); + args = cdr(args); + uint8_t aw = atomwidth(arg); + if (width < aw) w = 0; + else w = width - aw; + tilde = false; + if (ch2 == 'A') { + prin1object(arg, pfun); + indent(w, pad, pfun); + } else if (ch2 == 'S') { + printobject(arg, pfun); + indent(w, pad, pfun); + } else if (ch2 == 'D' || ch2 == 'G') { + indent(w, pad, pfun); + prin1object(arg, pfun); + } else if (ch2 == 'X' || ch2 == 'B') { + if (integerp(arg)) { + uint8_t base = (ch2 == 'B') ? 2 : 16; + uint8_t hw = basewidth(arg, base); + if (width < hw) w = 0; + else w = width - hw; + indent(w, pad, pfun); + pintbase(arg->integer, base, pfun); + } else { + indent(w, pad, pfun); + prin1object(arg, pfun); + } + } + tilde = false; + } else formaterr(formatstr, "invalid directive", n); + } + } else { + if (ch == '~') { + tilde = true; + pad = ' '; + width = 0; + comma = false; + quote = false; + } else if (!mute) pfun(ch); + } + n++; + } + if (output == nil) return obj; + else return nil; +} + +// LispLibrary + +/* + (require 'symbol) + Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library. + It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library. +*/ +object* fn_require(object* args, object* env) { + object* arg = first(args); + object* globals = GlobalEnv; + if (!symbolp(arg)) error(notasymbol, arg); + while (globals != NULL) { + object* pair = first(globals); + object* var = car(pair); + if (symbolp(var) && var == arg) return nil; + globals = cdr(globals); + } + GlobalStringIndex = 0; + object* line = read(glibrary); + while (line != NULL) { + // Is this the definition we want + symbol_t fname = first(line)->name; + if ((fname == sym(DEFUN) || fname == sym(DEFVAR)) && symbolp(second(line)) && second(line)->name == arg->name) { + eval(line, env); + return tee; + } + line = read(glibrary); + } + return nil; +} + +/* + (list-library) + Prints a list of the functions defined in the List Library. +*/ +object* fn_listlibrary(object* args, object* env) { + (void)args, (void)env; + GlobalStringIndex = 0; + object* line = read(glibrary); + while (line != NULL) { + builtin_t bname = builtin(first(line)->name); + if (bname == DEFUN || bname == DEFVAR) { + printsymbol(second(line), pserial); + pserial(' '); + } + line = read(glibrary); + } + return bsymbol(NOTHING); +} + +// Documentation + +/* + (? item) + Prints the documentation string of a built-in or user-defined function. +*/ +object* sp_help(object* args, object* env) { + if (args == NULL) error2(noargument); + object* docstring = documentation(first(args), env); + if (docstring) { + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printstring(docstring, pserial); + Flags = temp; + } + return bsymbol(NOTHING); +} + +/* + (documentation 'symbol [type]) + Returns the documentation string of a built-in or user-defined function. The type argument is ignored. +*/ +object* fn_documentation(object* args, object* env) { + return documentation(first(args), env); +} + +/* + (apropos item) + Prints the user-defined and built-in functions whose names contain the specified string or symbol. +*/ +object* fn_apropos(object* args, object* env) { + (void)env; + apropos(first(args), true); + return bsymbol(NOTHING); +} + +/* + (apropos-list item) + Returns a list of user-defined and built-in functions whose names contain the specified string or symbol. +*/ +object* fn_aproposlist(object* args, object* env) { + (void)env; + return apropos(first(args), false); +} + +// Error handling + +/* + (unwind-protect form1 [forms]*) + Evaluates form1 and forms in order and returns the value of form1, + but guarantees to evaluate forms even if an error occurs in form1. +*/ +object* sp_unwindprotect(object* args, object* env) { + if (args == NULL) error2(toofewargs); + object* current_GCStack = GCStack; + jmp_buf dynamic_handler; + jmp_buf* previous_handler = handler; + handler = &dynamic_handler; + object* protected_form = first(args); + object* result; + + bool signaled = false; + if (!setjmp(dynamic_handler)) { + result = eval(protected_form, env); + } else { + GCStack = current_GCStack; + signaled = true; + } + handler = previous_handler; + + object* protective_forms = cdr(args); + while (protective_forms != NULL) { + eval(car(protective_forms), env); + if (tstflag(RETURNFLAG)) break; + protective_forms = cdr(protective_forms); + } + + if (!signaled) return result; + GCStack = NULL; + longjmp(*handler, 1); +} + +/* + (ignore-errors [forms]*) + Evaluates forms ignoring errors. +*/ +object* sp_ignoreerrors(object* args, object* env) { + object* current_GCStack = GCStack; + jmp_buf dynamic_handler; + jmp_buf* previous_handler = handler; + handler = &dynamic_handler; + object* result = nil; + + bool muffled = tstflag(MUFFLEERRORS); + setflag(MUFFLEERRORS); + bool signaled = false; + if (!setjmp(dynamic_handler)) { + while (args != NULL) { + result = eval(car(args), env); + if (tstflag(RETURNFLAG)) break; + args = cdr(args); + } + } else { + GCStack = current_GCStack; + signaled = true; + } + handler = previous_handler; + if (!muffled) clrflag(MUFFLEERRORS); + + if (signaled) return bsymbol(NOTHING); + else return result; +} + +/* + (error controlstring [arguments]*) + Signals an error. The message is printed by format using the controlstring and arguments. +*/ +object* sp_error(object* args, object* env) { + object* message = eval(cons(bsymbol(FORMAT), cons(nil, args)), env); + if (!tstflag(MUFFLEERRORS)) { + flags_t temp = Flags; + clrflag(PRINTREADABLY); + pfstring("Error: ", pserial); + printstring(message, pserial); + Flags = temp; + pln(pserial); + } + GCStack = NULL; + longjmp(*handler, 1); +} + +// Wi-Fi + +/* + (with-client (str [address port]) form*) + Evaluates the forms with str bound to a wifi-stream. +*/ +object* sp_withclient(object* args, object* env) { + object* params = first(args); + object* var = first(params); + char buffer[BUFFERSIZE]; + params = cdr(params); + int n; + if (params == NULL) { + client = server.available(); + if (!client) return nil; + n = 2; + } else { + object* address = eval(first(params), env); + object* port = eval(second(params), env); + int success; + if (stringp(address)) success = client.connect(cstring(address, buffer, BUFFERSIZE), checkinteger(port)); + else if (integerp(address)) success = client.connect(address->integer, checkinteger(port)); + else error2("invalid address"); + if (!success) return nil; + n = 1; + } + object* pair = cons(var, stream(WIFISTREAM, n)); + push(pair, env); + object* forms = cdr(args); + object* result = progn_no_tc(forms, env); + client.stop(); + return result; +} + +/* + (available stream) + Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available. +*/ +object* fn_available(object* args, object* env) { + (void)env; + if (isstream(first(args)) >> 8 != WIFISTREAM) error2("invalid stream"); + return number(client.available()); +} + +/* + (wifi-server) + Starts a Wi-Fi server running. It returns nil. +*/ +object* fn_wifiserver(object* args, object* env) { + (void)args, (void)env; + server.begin(); + return nil; +} + +/* + (wifi-softap ssid [password channel hidden]) + Set up a soft access point to establish a Wi-Fi network. + Returns the IP address as a string or nil if unsuccessful. +*/ +object* fn_wifisoftap(object* args, object* env) { + (void)env; + char ssid[33], pass[65]; + if (args == NULL) return WiFi.softAPdisconnect(true) ? tee : nil; + object* first = first(args); + args = cdr(args); + if (args == NULL) WiFi.softAP(cstring(first, ssid, 33)); + else { + object* second = first(args); + args = cdr(args); + int channel = 1; + bool hidden = false; + if (args != NULL) { + channel = checkinteger(first(args)); + args = cdr(args); + if (args != NULL) hidden = (first(args) != nil); + } + WiFi.softAP(cstring(first, ssid, 33), cstring(second, pass, 65), channel, hidden); + } + return iptostring(WiFi.softAPIP()); +} + +/* + (connected stream) + Returns t or nil to indicate if the client on stream is connected. +*/ +object* fn_connected(object* args, object* env) { + (void)env; + if (isstream(first(args)) >> 8 != WIFISTREAM) error2("invalid stream"); + return client.connected() ? tee : nil; +} + +/* + (wifi-localip) + Returns the IP address of the local network as a string. +*/ +object* fn_wifilocalip(object* args, object* env) { + (void)args, (void)env; + return iptostring(WiFi.localIP()); +} + +/* + (wifi-connect [ssid pass]) + Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string. +*/ +object* fn_wificonnect(object* args, object* env) { + (void)env; + char ssid[33], pass[65]; + if (args == NULL) { + WiFi.disconnect(true); + return nil; + } + if (cdr(args) == NULL) WiFi.begin(cstring(first(args), ssid, 33)); + else WiFi.begin(cstring(first(args), ssid, 33), cstring(second(args), pass, 65)); + int result = WiFi.waitForConnectResult(); + if (result == WL_CONNECTED) return iptostring(WiFi.localIP()); + else if (result == WL_NO_SSID_AVAIL) error2("network not found"); + else if (result == WL_CONNECT_FAILED) error2("connection failed"); + else error2("unable to connect"); + return nil; +} + +// Graphics functions + +/* + (with-gfx (str) form*) + Evaluates the forms with str bound to an gfx-stream so you can print text + to the graphics display using the standard uLisp print commands. +*/ +object* sp_withgfx(object* args, object* env) { +#if defined(gfxsupport) + object* params = checkarguments(args, 1, 1); + object* var = first(params); + object* pair = cons(var, stream(GFXSTREAM, 1)); + push(pair, env); + object* forms = cdr(args); + object* result = progn_no_tc(forms, env); + return result; +#else + (void)args, (void)env; + error2("not supported"); + return nil; +#endif +} + +/* + (draw-pixel x y [colour]) + Draws a pixel at coordinates (x,y) in colour, or white if omitted. +*/ +object* fn_drawpixel(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t colour = COLOR_WHITE; + if (cddr(args) != NULL) colour = checkinteger(third(args)); + tft.drawPixel(checkinteger(first(args)), checkinteger(second(args)), colour); +#else + (void)args; +#endif + return nil; +} + +/* + (draw-line x0 y0 x1 y1 [colour]) + Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted. +*/ +object* fn_drawline(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[4], colour = COLOR_WHITE; + for (int i = 0; i < 4; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawLine(params[0], params[1], params[2], params[3], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (draw-rect x y w h [colour]) + Draws an outline rectangle with its top left corner at (x,y), with width w, + and with height h. The outline is drawn in colour, or white if omitted. +*/ +object* fn_drawrect(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[4], colour = COLOR_WHITE; + for (int i = 0; i < 4; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawRect(params[0], params[1], params[2], params[3], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (fill-rect x y w h [colour]) + Draws a filled rectangle with its top left corner at (x,y), with width w, + and with height h. The outline is drawn in colour, or white if omitted. +*/ +object* fn_fillrect(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[4], colour = COLOR_WHITE; + for (int i = 0; i < 4; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillRect(params[0], params[1], params[2], params[3], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (draw-circle x y r [colour]) + Draws an outline circle with its centre at (x, y) and with radius r. + The circle is drawn in colour, or white if omitted. +*/ +object* fn_drawcircle(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[3], colour = COLOR_WHITE; + for (int i = 0; i < 3; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawCircle(params[0], params[1], params[2], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (fill-circle x y r [colour]) + Draws a filled circle with its centre at (x, y) and with radius r. + The circle is drawn in colour, or white if omitted. +*/ +object* fn_fillcircle(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[3], colour = COLOR_WHITE; + for (int i = 0; i < 3; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillCircle(params[0], params[1], params[2], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (draw-round-rect x y w h radius [colour]) + Draws an outline rounded rectangle with its top left corner at (x,y), with width w, + height h, and corner radius radius. The outline is drawn in colour, or white if omitted. +*/ +object* fn_drawroundrect(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[5], colour = COLOR_WHITE; + for (int i = 0; i < 5; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawRoundRect(params[0], params[1], params[2], params[3], params[4], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (fill-round-rect x y w h radius [colour]) + Draws a filled rounded rectangle with its top left corner at (x,y), with width w, + height h, and corner radius radius. The outline is drawn in colour, or white if omitted. +*/ +object* fn_fillroundrect(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[5], colour = COLOR_WHITE; + for (int i = 0; i < 5; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillRoundRect(params[0], params[1], params[2], params[3], params[4], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (draw-triangle x0 y0 x1 y1 x2 y2 [colour]) + Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3). + The outline is drawn in colour, or white if omitted. +*/ +object* fn_drawtriangle(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[6], colour = COLOR_WHITE; + for (int i = 0; i < 6; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (fill-triangle x0 y0 x1 y1 x2 y2 [colour]) + Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3). + The outline is drawn in colour, or white if omitted. +*/ +object* fn_filltriangle(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t params[6], colour = COLOR_WHITE; + for (int i = 0; i < 6; i++) { + params[i] = checkinteger(car(args)); + args = cdr(args); + } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); +#else + (void)args; +#endif + return nil; +} + +/* + (draw-char x y char [colour background size]) + Draws the character char with its top left corner at (x,y). + The character is drawn in a 5 x 7 pixel font in colour against background, + which default to white and black respectively. + The character can optionally be scaled by size. +*/ +object* fn_drawchar(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t colour = COLOR_WHITE, bg = COLOR_BLACK, size = 1; + object* more = cdr(cddr(args)); + if (more != NULL) { + colour = checkinteger(car(more)); + more = cdr(more); + if (more != NULL) { + bg = checkinteger(car(more)); + more = cdr(more); + if (more != NULL) size = checkinteger(car(more)); + } + } + tft.drawChar(checkinteger(first(args)), checkinteger(second(args)), checkchar(third(args)), + colour, bg, size); +#else + (void)args; +#endif + return nil; +} + +/* + (set-cursor x y) + Sets the start point for text plotting to (x, y). +*/ +object* fn_setcursor(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + tft.setCursor(checkinteger(first(args)), checkinteger(second(args))); +#else + (void)args; +#endif + return nil; +} + +/* + (set-text-color colour [background]) + Sets the text colour for text plotted using (with-gfx ...). +*/ +object* fn_settextcolor(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + if (cdr(args) != NULL) tft.setTextColor(checkinteger(first(args)), checkinteger(second(args))); + else tft.setTextColor(checkinteger(first(args))); +#else + (void)args; +#endif + return nil; +} + +/* + (set-text-size scale) + Scales text by the specified size, default 1. +*/ +object* fn_settextsize(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + tft.setTextSize(checkinteger(first(args))); +#else + (void)args; +#endif + return nil; +} + +/* + (set-text-wrap boolean) + Specified whether text wraps at the right-hand edge of the display; the default is t. +*/ +object* fn_settextwrap(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + tft.setTextWrap(first(args) != NULL); +#else + (void)args; +#endif + return nil; +} + +/* + (fill-screen [colour]) + Fills or clears the screen with colour, default black. +*/ +object* fn_fillscreen(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + uint16_t colour = COLOR_BLACK; + if (args != NULL) colour = checkinteger(first(args)); + tft.fillScreen(colour); +#else + (void)args; +#endif + return nil; +} + +/* + (set-rotation option) + Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3. +*/ +object* fn_setrotation(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + tft.setRotation(checkinteger(first(args))); +#else + (void)args; +#endif + return nil; +} + +/* + (invert-display boolean) + Mirror-images the display. +*/ +object* fn_invertdisplay(object* args, object* env) { + (void)env; +#if defined(gfxsupport) + tft.invertDisplay(first(args) != NULL); +#else + (void)args; +#endif + return nil; +} + + +/* + (catch 'tag form*) + Evaluates the forms, and of any of them call (throw) with the same + tag, returns the "thrown" value. If none throw, returns the value returned by the + last form. +*/ +object* sp_catch(object* args, object* env) { + object* current_GCStack = GCStack; + + jmp_buf dynamic_handler; + jmp_buf* previous_handler = handler; + handler = &dynamic_handler; + + flags_t temp = Flags; + builtin_t catchcon = Context; + setflag(INCATCH); + + object* tag = first(args); + object* forms = rest(args); + protect(tag); + tag = eval(tag, env); + car(GCStack) = tag; + protect(forms); + + object* result; + + if (!setjmp(dynamic_handler)) { + // First: run forms + result = progn_no_tc(forms, env); + // If we get here nothing was thrown + GCStack = current_GCStack; + handler = previous_handler; + Flags = temp; + return result; + } else { + // Something was thrown, check if it is the same tag + GCStack = current_GCStack; + handler = previous_handler; + Flags = temp; + if (Thrown == NULL) { + // Not a (throw) --> propagate the error + longjmp(*handler, 1); + } else if (!eq(car(Thrown), tag)) { + // Wrong tag + if (tstflag(INCATCH)) { + // Try next-in-line catch + GCStack = NULL; + longjmp(*handler, 1); + } else { + // No upper catch + Context = catchcon; + error("no matching tag", car(Thrown)); + } + } else { + // Caught! + result = cdr(Thrown); + Thrown = NULL; + return result; + } + } +} + +/* + (throw 'tag [value]) + Exits the (catch) form opened with the same tag (using eq). + It is an error to call (throw) without first entering a (catch) with + the same tag. +*/ +object* fn_throw(object* args, object* env) { + if (!tstflag(INCATCH)) error2("not in a catch"); + object* tag = first(args); + args = rest(args); + object* value = NULL; + if (args != NULL) value = first(args); + Thrown = cons(tag, value); + longjmp(*handler, 1); + // unreachable + return NULL; +} + +// BACKQUOTE support +// see https://github.com/kanaka/mal/blob/master/process/guide.md#step-7-quoting +// and https://github.com/kanaka/mal/issues/103#issuecomment-159047401 + +object* reverse(object* what) { + object* result = NULL; + for (; what != NULL; what = cdr(what)) { + push(car(what), result); + } + return result; +} + +object* process_backquote(object* arg, size_t level = 0) { + // "If ast is a map or a symbol, return a list containing: the "quote" symbol, then ast." + if (arg == NULL || atom(arg)) return quoteit(QUOTE, arg); + // "If ast is a list starting with the "unquote" symbol, return its second element." + if (listp(arg) && symbolp(first(arg))) { + switch (builtin(first(arg)->name)) { + case BACKQUOTE: return process_backquote(second(arg), level + 1); + case UNQUOTE: return level == 0 ? second(arg) : process_backquote(second(arg), level - 1); + default: break; + } + } + // "If ast is a list failing previous test, the result will be a list populated by the following process." + // "The result is initially an empty list. Iterate over each element elt of ast in reverse order:" + object* result = NULL; + object* rev_arg = reverse(arg); + for (; rev_arg != NULL; rev_arg = cdr(rev_arg)) { + object* element = car(rev_arg); + // "If elt is a list starting with the "splice-unquote" symbol, + // replace the current result with a list containing: the "concat" symbol, + // the second element of elt, then the previous result." + if (listp(element) && symbolp(first(element)) && builtin(first(element)->name) == UNQUOTE_SPLICING) { + object* x = second(element); + if (level > 0) x = process_backquote(x, level - 1); + result = cons(bsymbol(APPEND), cons(x, cons(result, nil))); + } + // "Else replace the current result with a list containing: + // the "cons" symbol, the result of calling quasiquote with + // elt as argument, then the previous result." + else + result = cons(bsymbol(CONS), cons(process_backquote(element, level), cons(result, nil))); + } + return result; +} + +// "Add the quasiquote special form. This form does the same than quasiquoteexpand, +// but evaluates the result in the current environment before returning it, either by +// recursively calling EVAL with the result and env, or by assigning ast with the result +// and continuing execution at the top of the loop (TCO)." +object* sp_backquote(object* args, object* env) { + object* result = process_backquote(first(args)); + setflag(TAILCALL); + return result; +} + +object* bq_invalid(object* args, object* env) { + (void)args, (void)env; + error2("not valid outside backquote"); + // unreachable + return NULL; +} + +//////////////////////////////////////////////////////////////////////// +// MACRO support + +bool is_macro_call(object* form, object* env) { + if (form == nil) return false; +CHECK: + if (symbolp(car(form))) { + object* pair = findpair(car(form), env); + if (pair == NULL) return false; + form = cons(cdr(pair), cdr(form)); + goto CHECK; + } + if (!consp(form)) return false; + object* lambda = first(form); + if (!consp(lambda)) return false; + return isbuiltin(first(lambda), MACRO); +} + +object* macroexpand1(object* form, object* env, bool* done) { + if (!is_macro_call(form, env)) { + *done = true; + return form; + } + while (symbolp(car(form))) form = cons(cdr(findvalue(car(form), env)), cdr(form)); + protect(form); + form = closure(false, sym(NIL), car(form), cdr(form), &env); + clrflag(TAILCALL); + object* result = eval(form, env); + unprotect(); + return result; +} + +object* fn_macroexpand1(object* args, object* env) { + bool dummy; + return macroexpand1(first(args), env, &dummy); +} + +object* macroexpand(object* form, object* env) { + bool done = false; + protect(form); + while (!done) { + form = macroexpand1(form, env, &done); + car(GCStack) = form; + } + unprotect(); + return form; +} + +object* fn_macroexpand(object* args, object* env) { + return macroexpand(first(args), env); +} + +/////////////////////////////////////////////////////////// + +// Built-in symbol names +const char string0[] = "nil"; +const char string1[] = "t"; +const char string2[] = "nothing"; +const char string3[] = "&optional"; +const char stringfeatures[] = "*features*"; +const char string4[] = ":initial-element"; +const char string5[] = ":element-type"; +const char stringtest[] = ":test"; +const char string6[] = "bit"; +const char string7[] = "&rest"; +const char string8[] = "lambda"; +const char stringmacro[] = "macro"; +const char string9[] = "let"; +const char string10[] = "let*"; +const char string11[] = "closure"; +const char string12[] = "*pc*"; +const char string13[] = "quote"; +const char stringbackquote[] = "backquote"; +const char stringunquote[] = "unquote"; +const char stringuqsplicing[] = "unquote-splicing"; +const char string57[] = "cons"; +const char string92[] = "append"; +const char string14[] = "defun"; +const char string15[] = "defvar"; +const char stringdefmacro[] = "defmacro"; +const char string16[] = "car"; +const char string17[] = "first"; +const char string18[] = "cdr"; +const char string19[] = "rest"; +const char string20[] = "nth"; +const char string21[] = "aref"; +const char string22[] = "string"; +const char string23[] = "pinmode"; +const char string24[] = "digitalwrite"; +const char string25[] = "analogread"; +const char string26[] = "register"; +const char string27[] = "format"; +const char string28[] = "or"; +const char string29[] = "setq"; +const char string30[] = "loop"; +const char string31[] = "return"; +const char string32[] = "push"; +const char string33[] = "pop"; +const char string34[] = "incf"; +const char string35[] = "decf"; +const char string36[] = "setf"; +const char string37[] = "dolist"; +const char string38[] = "dotimes"; +const char stringdo[] = "do"; +const char stringdostar[] = "do*"; +const char string39[] = "trace"; +const char string40[] = "untrace"; +const char string41[] = "for-millis"; +const char string42[] = "time"; +const char string43[] = "with-output-to-string"; +const char string44[] = "with-serial"; +const char string45[] = "with-i2c"; +const char string46[] = "with-spi"; +const char string47[] = "with-sd-card"; +const char string48[] = "progn"; +const char string49[] = "if"; +const char string50[] = "cond"; +const char string51[] = "when"; +const char string52[] = "unless"; +const char string53[] = "case"; +const char string54[] = "and"; +const char string55[] = "not"; +const char string56[] = "null"; +const char string58[] = "atom"; +const char string59[] = "listp"; +const char string60[] = "consp"; +const char string61[] = "symbolp"; +const char string62[] = "arrayp"; +const char string63[] = "boundp"; +const char string64[] = "keywordp"; +const char string65[] = "set"; +const char string66[] = "streamp"; +const char string67[] = "eq"; +const char string68[] = "equal"; +const char string69[] = "caar"; +const char string70[] = "cadr"; +const char string71[] = "second"; +const char string72[] = "cdar"; +const char string73[] = "cddr"; +const char string74[] = "caaar"; +const char string75[] = "caadr"; +const char string76[] = "cadar"; +const char string77[] = "caddr"; +const char string78[] = "third"; +const char string79[] = "cdaar"; +const char string80[] = "cdadr"; +const char string81[] = "cddar"; +const char string82[] = "cdddr"; +const char string83[] = "length"; +const char string84[] = "array-dimensions"; +const char string85[] = "list"; +const char stringcopylist[] = "copy-list"; +const char string86[] = "make-array"; +const char string87[] = "reverse"; +const char string88[] = "assoc"; +const char string89[] = "member"; +const char string90[] = "apply"; +const char string91[] = "funcall"; +const char string93[] = "mapc"; +const char string94[] = "mapcar"; +const char stringmapl[] = "mapl"; +const char string95[] = "mapcan"; +const char stringmaplist[] = "maplist"; +const char stringmapcon[] = "mapcon"; +const char string96[] = "+"; +const char string97[] = "-"; +const char string98[] = "*"; +const char string99[] = "/"; +const char string100[] = "mod"; +const char string101[] = "1+"; +const char string102[] = "1-"; +const char string103[] = "abs"; +const char string104[] = "random"; +const char string105[] = "max"; +const char string106[] = "min"; +const char string107[] = "/="; +const char string108[] = "="; +const char string109[] = "<"; +const char string110[] = "<="; +const char string111[] = ">"; +const char string112[] = ">="; +const char string113[] = "plusp"; +const char string114[] = "minusp"; +const char string115[] = "zerop"; +const char string116[] = "oddp"; +const char string117[] = "evenp"; +const char string118[] = "integerp"; +const char string119[] = "numberp"; +const char string120[] = "float"; +const char string121[] = "floatp"; +const char string122[] = "sin"; +const char string123[] = "cos"; +const char string124[] = "tan"; +const char string125[] = "asin"; +const char string126[] = "acos"; +const char string127[] = "atan"; +const char string128[] = "sinh"; +const char string129[] = "cosh"; +const char string130[] = "tanh"; +const char string131[] = "exp"; +const char string132[] = "sqrt"; +const char string133[] = "log"; +const char string134[] = "expt"; +const char string135[] = "ceiling"; +const char string136[] = "floor"; +const char string137[] = "truncate"; +const char string138[] = "round"; +const char string139[] = "char"; +const char string140[] = "char-code"; +const char string141[] = "code-char"; +const char string142[] = "characterp"; +const char string143[] = "stringp"; +const char string144[] = "string="; +const char string145[] = "string<"; +const char string146[] = "string>"; +const char stringstringnoteq[] = "string/="; +const char stringstringlesseq[] = "string<="; +const char stringstringgteq[] = "string>="; +const char string147[] = "sort"; +const char string148[] = "concatenate"; +const char string149[] = "subseq"; +const char string150[] = "search"; +const char string151[] = "read-from-string"; +const char string152[] = "princ-to-string"; +const char string153[] = "prin1-to-string"; +const char string154[] = "logand"; +const char string155[] = "logior"; +const char string156[] = "logxor"; +const char string157[] = "lognot"; +const char string158[] = "ash"; +const char string159[] = "logbitp"; +const char string160[] = "eval"; +const char string161[] = "globals"; +const char string162[] = "locals"; +const char string163[] = "makunbound"; +const char string164[] = "break"; +const char string165[] = "read"; +const char string166[] = "prin1"; +const char string167[] = "print"; +const char string168[] = "princ"; +const char string169[] = "terpri"; +const char string170[] = "read-byte"; +const char string171[] = "read-line"; +const char string172[] = "write-byte"; +const char string173[] = "write-string"; +const char string174[] = "write-line"; +const char string175[] = "restart-i2c"; +const char string176[] = "gc"; +const char string177[] = "room"; +const char string178[] = "save-image"; +const char string179[] = "load-image"; +const char string180[] = "cls"; +const char string181[] = "digitalread"; +const char string182[] = "analogreadresolution"; +const char string183[] = "analogwrite"; +const char string184[] = "delay"; +const char string185[] = "millis"; +const char string186[] = "sleep"; +const char string187[] = "note"; +const char string188[] = "edit"; +const char string189[] = "pprint"; +const char string190[] = "pprintall"; +const char string191[] = "require"; +const char string192[] = "list-library"; +const char string193[] = "?"; +const char string194[] = "documentation"; +const char string195[] = "apropos"; +const char string196[] = "apropos-list"; +const char string197[] = "unwind-protect"; +const char string198[] = "ignore-errors"; +const char string199[] = "error"; +const char string200[] = "with-client"; +const char string201[] = "available"; +const char string202[] = "wifi-server"; +const char string203[] = "wifi-softap"; +const char string204[] = "connected"; +const char string205[] = "wifi-localip"; +const char string206[] = "wifi-connect"; +const char string207[] = "with-gfx"; +const char string208[] = "draw-pixel"; +const char string209[] = "draw-line"; +const char string210[] = "draw-rect"; +const char string211[] = "fill-rect"; +const char string212[] = "draw-circle"; +const char string213[] = "fill-circle"; +const char string214[] = "draw-round-rect"; +const char string215[] = "fill-round-rect"; +const char string216[] = "draw-triangle"; +const char string217[] = "fill-triangle"; +const char string218[] = "draw-char"; +const char string219[] = "set-cursor"; +const char string220[] = "set-text-color"; +const char string221[] = "set-text-size"; +const char string222[] = "set-text-wrap"; +const char string223[] = "fill-screen"; +const char string224[] = "set-rotation"; +const char string225[] = "invert-display"; +const char string226[] = ":led-builtin"; +const char string227[] = ":high"; +const char string228[] = ":low"; +const char string229[] = ":input"; +const char string230[] = ":input-pullup"; +const char string231[] = ":input-pulldown"; +const char string232[] = ":output"; + +const char stringcatch[] = "catch"; +const char stringthrow[] = "throw"; +const char stringmacroexpand1[] = "macroexpand-1"; +const char stringmacroexpand[] = "macroexpand"; + +// Documentation strings +const char doc0[] = "nil\n" + "A symbol equivalent to the empty list (). Also represents false."; +const char doc1[] = "t\n" + "A symbol representing true."; +const char doc2[] = "nothing\n" + "A symbol with no value.\n" + "It is useful if you want to suppress printing the result of evaluating a function."; +const char doc3[] = "&optional\n" + "Can be followed by one or more optional parameters in a lambda or defun parameter list."; +const char docfeatures[] = "*features*\n" + "Expands to a list of keywords representing features supported by this platform."; +const char doc7[] = "&rest\n" + "Can be followed by a parameter in a lambda or defun parameter list,\n" + "and is assigned a list of the corresponding arguments."; +const char doc8[] = "(lambda (parameter*) form*)\n" + "Creates an unnamed function with parameters. The body is evaluated with the parameters as local variables\n" + "whose initial values are defined by the values of the forms after the lambda form."; +const char docmacro[] = "(macro (parameter*) form*)\n" + "Creates an unnamed lambda-macro with parameters. The body is evaluated with the parameters as local variables\n" + "whose initial values are defined by the values of the forms after the macro form;\n" + "the resultant Lisp code returned is then evaluated again, this time in the scope of where the macro was called."; +const char doc9[] = "(let ((var value) ... ) forms*)\n" + "Declares local variables with values, and evaluates the forms with those local variables."; +const char doc10[] = "(let* ((var value) ... ) forms*)\n" + "Declares local variables with values, and evaluates the forms with those local variables.\n" + "Each declaration can refer to local variables that have been defined earlier in the let*."; +const char docbackquote[] = "(backquote form) or `form\n" + "Expands the unquotes present in the form as a syntactic template. Most commonly used in macros."; +const char docunquote[] = "(unquote form) or ,form\n" + "Marks a form to be evaluated and the value inserted when (backquote) expands the template."; +const char docunquotesplicing[] = "(unquote-splicing form) or ,@form\n" + "Marks a form to be evaluated and the value spliced in when (backquote) expands the template.\n" + "If the value returned when evaluating form is not a proper list (backquote) will bork very badly."; +const char doc57[] = "(cons item item)\n" + "If the second argument is a list, cons returns a new list with item added to the front of the list.\n" + "If the second argument isn't a list cons returns a dotted pair."; +const char doc92[] = "(append list*)\n" + "Joins its arguments, which should be lists, into a single list."; +const char doc14[] = "(defun name (parameters) form*)\n" + "Defines a function."; +const char doc15[] = "(defvar variable form)\n" + "Defines a global variable."; +const char docdefmacro[] = "(defmacro name (parameters) form*)\n" + "Defines a syntactic macro."; +const char doceq[] = "(eq item item)\n" + "Tests whether the two arguments are the same symbol, same character, equal numbers,\n" + "or point to the same cons, and returns t or nil as appropriate."; +const char doc16[] = "(car list)\n" + "Returns the first item in a list."; +const char doc18[] = "(cdr list)\n" + "Returns a list with the first item removed."; +const char doc20[] = "(nth number list)\n" + "Returns the nth item in list, counting from zero."; +const char doc21[] = "(aref array index [index*])\n" + "Returns an element from the specified array."; +const char docchar[] = "(char string n)\n" + "Returns the nth character in a string, counting from zero."; +const char doc22[] = "(string item)\n" + "Converts its argument to a string."; +const char doc23[] = "(pinmode pin mode)\n" + "Sets the input/output mode of an Arduino pin number, and returns nil.\n" + "The mode parameter can be an integer, a keyword, or t or nil."; +const char doc24[] = "(digitalwrite pin state)\n" + "Sets the state of the specified Arduino pin number."; +const char doc25[] = "(analogread pin)\n" + "Reads the specified Arduino analogue pin number and returns the value."; +const char doc26[] = "(register address [value])\n" + "Reads or writes the value of a peripheral register.\n" + "If value is not specified the function returns the value of the register at address.\n" + "If value is specified the value is written to the register at address and the function returns value."; +const char doc27[] = "(format output controlstring [arguments]*)\n" + "Outputs its arguments formatted according to the format directives in controlstring."; +const char doc28[] = "(or item*)\n" + "Evaluates its arguments until one returns non-nil, and returns its value."; +const char doc29[] = "(setq symbol value [symbol value]*)\n" + "For each pair of arguments assigns the value of the second argument\n" + "to the variable specified in the first argument."; +const char doc30[] = "(loop forms*)\n" + "Executes its arguments repeatedly until one of the arguments calls (return),\n" + "which then causes an exit from the loop."; +const char doc31[] = "(return [value])\n" + "Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value."; +const char doc32[] = "(push item place)\n" + "Modifies the value of place, which should be a list, to add item onto the front of the list,\n" + "and returns the new list."; +const char doc33[] = "(pop place)\n" + "Modifies the value of place, which should be a list, to remove its first item, and returns that item."; +const char doc34[] = "(incf place [number])\n" + "Increments a place, which should have an numeric value, and returns the result.\n" + "The third argument is an optional increment which defaults to 1."; +const char doc35[] = "(decf place [number])\n" + "Decrements a place, which should have an numeric value, and returns the result.\n" + "The third argument is an optional decrement which defaults to 1."; +const char doc36[] = "(setf place value [place value]*)\n" + "For each pair of arguments modifies a place to the result of evaluating value."; +const char doc37[] = "(dolist (var list [result]) form*)\n" + "Sets the local variable var to each element of list in turn, and executes the forms.\n" + "It then returns result, or nil if result is omitted."; +const char doc38[] = "(dotimes (var number [result]) form*)\n" + "Executes the forms number times, with the local variable var set to each integer from 0 to number-1 in turn.\n" + "It then returns result, or nil if result is omitted."; +const char docdo[] PROGMEM = "(do ((var [init [step]])*) (end-test result*) form*)\n" + "Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step sequentially.\n" + "The forms are executed until end-test is true. It returns result."; +const char docdostar[] PROGMEM = "(do* ((var [init [step]])*) (end-test result*) form*)\n" + "Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step in parallel.\n" + "The forms are executed until end-test is true. It returns result."; +const char doc39[] = "(trace [function]*)\n" + "Turns on tracing of up to " stringify(TRACEMAX) " user-defined functions,\n" + "and returns a list of the functions currently being traced."; +const char doc40[] = "(untrace [function]*)\n" + "Turns off tracing of up to " stringify(TRACEMAX) " user-defined functions, and returns a list of the functions untraced.\n" + "If no functions are specified it untraces all functions."; +const char doc41[] = "(for-millis ([number]) form*)\n" + "Executes the forms and then waits until a total of number milliseconds have elapsed.\n" + "Returns the total number of milliseconds taken."; +const char doc42[] = "(time form)\n" + "Prints the value returned by the form, and the time taken to evaluate the form\n" + "in milliseconds or seconds."; +const char doc43[] = "(with-output-to-string (str) form*)\n" + "Returns a string containing the output to the stream variable str."; +const char doc44[] = "(with-serial (str port [baud]) form*)\n" + "Evaluates the forms with str bound to a serial-stream using port.\n" + "The optional baud gives the baud rate divided by 100, default 96."; +const char doc45[] = "(with-i2c (str [port] address [read-p]) form*)\n" + "Evaluates the forms with str bound to an i2c-stream defined by address.\n" + "If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes\n" + "to be read from the stream. The port if specified is ignored."; +const char doc46[] = "(with-spi (str pin [clock] [bitorder] [mode]) form*)\n" + "Evaluates the forms with str bound to an spi-stream.\n" + "The parameters specify the enable pin, clock in kHz (default 4000),\n" + "bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), and SPI mode (default 0)."; +const char doc47[] = "(with-sd-card (str filename [mode]) form*)\n" + "Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename.\n" + "If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite."; +const char doc48[] = "(progn form*)\n" + "Evaluates several forms grouped together into a block, and returns the result of evaluating the last form."; +const char doc49[] = "(if test then [else])\n" + "Evaluates test. If it's non-nil the form then is evaluated and returned;\n" + "otherwise the form else is evaluated and returned."; +const char doc50[] = "(cond ((test form*) (test form*) ... ))\n" + "Each argument is a list consisting of a test optionally followed by one or more forms.\n" + "If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond.\n" + "If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way."; +const char doc51[] = "(when test form*)\n" + "Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned."; +const char doc52[] = "(unless test form*)\n" + "Evaluates the test. If it's nil the forms are evaluated and the last value is returned."; +const char doc53[] = "(case keyform ((key form*) (key form*) ... ))\n" + "Evaluates a keyform to produce a test key, and then tests this against a series of arguments,\n" + "each of which is a list containing a key optionally followed by one or more forms."; +const char doc54[] = "(and item*)\n" + "Evaluates its arguments until one returns nil, and returns the last value."; +const char doc55[] = "(not item)\n" + "Returns t if its argument is nil, or nil otherwise. Equivalent to null."; +const char doc58[] = "(atom item)\n" + "Returns t if its argument is a single number, symbol, or nil."; +const char doc59[] = "(listp item)\n" + "Returns t if its argument is a list."; +const char doc60[] = "(consp item)\n" + "Returns t if its argument is a non-null list."; +const char doc61[] = "(symbolp item)\n" + "Returns t if its argument is a symbol."; +const char doc62[] = "(arrayp item)\n" + "Returns t if its argument is an array."; +const char doc63[] = "(boundp item)\n" + "Returns t if its argument is a symbol with a value."; +const char doc64[] = "(keywordp item)\n" + "Returns t if its argument is a built-in or user-defined keyword."; +const char doc65[] = "(set symbol value [symbol value]*)\n" + "For each pair of arguments, assigns the value of the second argument to the value of the first argument."; +const char doc66[] = "(streamp item)\n" + "Returns t if its argument is a stream."; +const char doc67[] = "(eq item item)\n" + "Tests whether the two arguments are the same symbol, same character, equal numbers,\n" + "or point to the same cons, and returns t or nil as appropriate."; +const char doc68[] = "(equal item item)\n" + "Tests whether the two arguments are the same symbol, same character, equal numbers,\n" + "or point to the same cons, and returns t or nil as appropriate."; +const char doc69[] = "(caar list)"; +const char doc70[] = "(cadr list)"; +const char doc72[] = "(cdar list)\n" + "Equivalent to (cdr (car list))."; +const char doc73[] = "(cddr list)\n" + "Equivalent to (cdr (cdr list))."; +const char doc74[] = "(caaar list)\n" + "Equivalent to (car (car (car list)))."; +const char doc75[] = "(caadr list)\n" + "Equivalent to (car (car (cdar list)))."; +const char doc76[] = "(cadar list)\n" + "Equivalent to (car (cdr (car list)))."; +const char doc77[] = "(caddr list)\n" + "Equivalent to (car (cdr (cdr list)))."; +const char doc79[] = "(cdaar list)\n" + "Equivalent to (cdar (car (car list)))."; +const char doc80[] = "(cdadr list)\n" + "Equivalent to (cdr (car (cdr list)))."; +const char doc81[] = "(cddar list)\n" + "Equivalent to (cdr (cdr (car list)))."; +const char doc82[] = "(cdddr list)\n" + "Equivalent to (cdr (cdr (cdr list)))."; +const char doc83[] = "(length item)\n" + "Returns the number of items in a list, the length of a string, or the length of a one-dimensional array."; +const char doc84[] = "(array-dimensions item)\n" + "Returns a list of the dimensions of an array."; +const char doc85[] = "(list item*)\n" + "Returns a list of the values of its arguments."; +const char doccopylist[] = "(copy-list list)\n" + "Returns a copy of a list."; +const char doc86[] = "(make-array size [:initial-element element] [:element-type 'bit])\n" + "If size is an integer it creates a one-dimensional array with elements from 0 to size-1.\n" + "If size is a list of n integers it creates an n-dimensional array with those dimensions.\n" + "If :element-type 'bit is specified the array is a bit array."; +const char doc87[] = "(reverse list)\n" + "Returns a list with the elements of list in reverse order."; +const char doc88[] = "(assoc key list [:test function])\n" + "Looks up a key in an association list of (key . value) pairs, using eq or the specified test function,\n" + "and returns the matching pair, or nil if no pair is found."; +const char doc89[] = "(member item list [:test function])\n" + "Searches for an item in a list, using eq or the specified test function, and returns the list starting\n" + "or nil if it is not found."; +const char doc90[] = "(apply function list)\n" + "Returns the result of evaluating function, with the list of arguments specified by the second parameter."; +const char doc91[] = "(funcall function argument*)\n" + "Evaluates function with the specified arguments."; +const char doc93[] = "(mapc function list1 [list]*)\n" + "Applies the function to each element in one or more lists, ignoring the results.\n" + "It returns the first list argument."; +const char docmapl[] = "(mapl function list1 [list]*)\n" + "Applies the function to one or more lists and then successive cdrs of those lists,\n" + "ignoring the results. It returns the first list argument."; +const char doc94[] = "(mapcar function list1 [list]*)\n" + "Applies the function to each element in one or more lists, and returns the resulting list."; +const char doc95[] = "(mapcan function list1 [list]*)\n" + "Applies the function to each element in one or more lists. The results should be lists,\n" + "and these are destructively nconc'ed together to give the value returned."; +const char docmaplist[] = "(maplist function list1 [list]*)\n" + "Applies the function to one or more lists and then successive cdrs of those lists,\n" + "and returns the resulting list."; +const char docmapcon[] = "(mapcon function list1 [list]*)\n" + "Applies the function to one or more lists and then successive cdrs of those lists,\n" + "and these are destructively concatenated together to give the value returned."; +const char doc96[] = "(+ number*)\n" + "Adds its arguments together.\n" + "If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n" + "otherwise a floating-point number."; +const char doc97[] = "(- number*)\n" + "If there is one argument, negates the argument.\n" + "If there are two or more arguments, subtracts the second and subsequent arguments from the first argument.\n" + "If each argument is an integer, and the running total doesn't overflow, returns the result as an integer,\n" + "otherwise a floating-point number."; +const char doc98[] = "(* number*)\n" + "Multiplies its arguments together.\n" + "If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n" + "otherwise it's a floating-point number."; +const char doc99[] = "(/ number*)\n" + "Divides the first argument by the second and subsequent arguments.\n" + "If each argument is an integer, and each division produces an exact result, the result is an integer;\n" + "otherwise it's a floating-point number."; +const char doc100[] = "(mod number number)\n" + "Returns its first argument modulo the second argument.\n" + "If both arguments are integers the result is an integer; otherwise it's a floating-point number."; +const char doc101[] = "(1+ number)\n" + "Adds one to its argument and returns it.\n" + "If the argument is an integer the result is an integer if possible;\n" + "otherwise it's a floating-point number."; +const char doc102[] = "(1- number)\n" + "Subtracts one from its argument and returns it.\n" + "If the argument is an integer the result is an integer if possible;\n" + "otherwise it's a floating-point number."; +const char doc103[] = "(abs number)\n" + "Returns the absolute, positive value of its argument.\n" + "If the argument is an integer the result will be returned as an integer if possible,\n" + "otherwise a floating-point number."; +const char doc104[] = "(random number)\n" + "If number is an integer returns a random number between 0 and one less than its argument.\n" + "Otherwise returns a floating-point number between zero and number."; +const char doc105[] = "(max number*)\n" + "Returns the maximum of one or more arguments."; +const char doc106[] = "(min number*)\n" + "Returns the minimum of one or more arguments."; +const char doc107[] = "(/= number*)\n" + "Returns t if none of the arguments are equal, or nil if two or more arguments are equal."; +const char doc108[] = "(= number*)\n" + "Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise."; +const char doc109[] = "(< number*)\n" + "Returns t if each argument is less than the next argument, and nil otherwise."; +const char doc110[] = "(<= number*)\n" + "Returns t if each argument is less than or equal to the next argument, and nil otherwise."; +const char doc111[] = "(> number*)\n" + "Returns t if each argument is greater than the next argument, and nil otherwise."; +const char doc112[] = "(>= number*)\n" + "Returns t if each argument is greater than or equal to the next argument, and nil otherwise."; +const char doc113[] = "(plusp number)\n" + "Returns t if the argument is greater than zero, or nil otherwise."; +const char doc114[] = "(minusp number)\n" + "Returns t if the argument is less than zero, or nil otherwise."; +const char doc115[] = "(zerop number)\n" + "Returns t if the argument is zero."; +const char doc116[] = "(oddp number)\n" + "Returns t if the integer argument is odd."; +const char doc117[] = "(evenp number)\n" + "Returns t if the integer argument is even."; +const char doc118[] = "(integerp number)\n" + "Returns t if the argument is an integer."; +const char doc119[] = "(numberp number)\n" + "Returns t if the argument is a number."; +const char doc120[] = "(float number)\n" + "Returns its argument converted to a floating-point number."; +const char doc121[] = "(floatp number)\n" + "Returns t if the argument is a floating-point number."; +const char doc122[] = "(sin number)\n" + "Returns sin(number)."; +const char doc123[] = "(cos number)\n" + "Returns cos(number)."; +const char doc124[] = "(tan number)\n" + "Returns tan(number)."; +const char doc125[] = "(asin number)\n" + "Returns asin(number)."; +const char doc126[] = "(acos number)\n" + "Returns acos(number)."; +const char doc127[] = "(atan number1 [number2])\n" + "Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1."; +const char doc128[] = "(sinh number)\n" + "Returns sinh(number)."; +const char doc129[] = "(cosh number)\n" + "Returns cosh(number)."; +const char doc130[] = "(tanh number)\n" + "Returns tanh(number)."; +const char doc131[] = "(exp number)\n" + "Returns exp(number)."; +const char doc132[] = "(sqrt number)\n" + "Returns sqrt(number)."; +const char doc133[] = "(log number [base])\n" + "Returns the logarithm of number to the specified base. If base is omitted it defaults to e."; +const char doc134[] = "(expt number power)\n" + "Returns number raised to the specified power.\n" + "Returns the result as an integer if the arguments are integers and the result will be within range,\n" + "otherwise a floating-point number."; +const char doc135[] = "(ceiling number [divisor])\n" + "Returns ceil(number/divisor). If omitted, divisor is 1."; +const char doc136[] = "(floor number [divisor])\n" + "Returns floor(number/divisor). If omitted, divisor is 1."; +const char doc137[] = "(truncate number [divisor])\n" + "Returns the integer part of number/divisor. If divisor is omitted it defaults to 1."; +const char doc138[] = "(round number [divisor])\n" + "Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1."; +const char doc139[] = "(char string n)\n" + "Returns the nth character in a string, counting from zero."; +const char doc140[] = "(char-code character)\n" + "Returns the ASCII code for a character, as an integer."; +const char doc141[] = "(code-char integer)\n" + "Returns the character for the specified ASCII code."; +const char doc142[] = "(characterp item)\n" + "Returns t if the argument is a character and nil otherwise."; +const char doc143[] = "(stringp item)\n" + "Returns t if the argument is a string and nil otherwise."; +const char doc144[] = "(string= string string)\n" + "Returns t if the two strings are the same, or nil otherwise."; +const char doc145[] = "(string< string string)\n" + "Returns the index to the first mismatch if the first string is alphabetically less than the second string,\n" + "or nil otherwise."; +const char doc146[] = "(string> string string)\n" + "Returns the index to the first mismatch if the first string is alphabetically greater than the second string,\n" + "or nil otherwise."; +const char docstringnoteq[] = "(string/= string string)\n" + "Returns the index to the first mismatch if the two strings are not the same, or nil otherwise."; +const char docstringlteq[] = "(string<= string string)\n" + "Returns the index to the first mismatch if the first string is alphabetically less than or equal to\n" + "the second string, or nil otherwise."; +const char docstringgteq[] = "(string>= string string)\n" + "Returns the index to the first mismatch if the first string is alphabetically greater than or equal to\n" + "the second string, or nil otherwise."; +const char doc147[] = "(sort list test)\n" + "Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list."; +const char doc148[] = "(concatenate 'string string*)\n" + "Joins together the strings given in the second and subsequent arguments, and returns a single string."; +const char doc149[] = "(subseq seq start [end])\n" + "Returns a subsequence of a list or string from item start to item end-1."; +const char doc150[] = "(search pattern target [:test function])\n" + "Returns the index of the first occurrence of pattern in target, or nil if it's not found.\n" + "The target can be a list or string. If it's a list a test function can be specified; default eq."; +const char doc151[] = "(read-from-string string)\n" + "Reads an atom or list from the specified string and returns it."; +const char doc152[] = "(princ-to-string item)\n" + "Prints its argument to a string, and returns the string.\n" + "Characters and strings are printed without quotation marks or escape characters."; +const char doc153[] = "(prin1-to-string item [stream])\n" + "Prints its argument to a string, and returns the string.\n" + "Characters and strings are printed with quotation marks and escape characters,\n" + "in a format that will be suitable for read-from-string."; +const char doc154[] = "(logand [value*])\n" + "Returns the bitwise & of the values."; +const char doc155[] = "(logior [value*])\n" + "Returns the bitwise | of the values."; +const char doc156[] = "(logxor [value*])\n" + "Returns the bitwise ^ of the values."; +const char doc157[] = "(lognot value)\n" + "Returns the bitwise logical NOT of the value."; +const char doc158[] = "(ash value shift)\n" + "Returns the result of bitwise shifting value by shift bits. If shift is positive, value is shifted to the left."; +const char doc159[] = "(logbitp bit value)\n" + "Returns t if bit number bit in value is a '1', and nil if it is a '0'."; +const char doc160[] = "(eval form*)\n" + "Evaluates its argument an extra time."; +const char doc161[] = "(globals)\n" + "Returns a list of global variables."; +const char doc162[] = "(locals)\n" + "Returns an association list of local variables and their values."; +const char doc163[] = "(makunbound symbol)\n" + "Removes the value of the symbol from GlobalEnv and returns the symbol."; +const char doc164[] = "(break)\n" + "Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL."; +const char doc165[] = "(read [stream])\n" + "Reads an atom or list from the serial input and returns it.\n" + "If stream is specified the item is read from the specified stream."; +const char doc166[] = "(prin1 item [stream])\n" + "Prints its argument, and returns its value.\n" + "Strings are printed with quotation marks and escape characters."; +const char doc167[] = "(print item [stream])\n" + "Prints its argument with quotation marks and escape characters, on a new line, and followed by a space.\n" + "If stream is specified the argument is printed to the specified stream."; +const char doc168[] = "(princ item [stream])\n" + "Prints its argument, and returns its value.\n" + "Characters and strings are printed without quotation marks or escape characters."; +const char doc169[] = "(terpri [stream])\n" + "Prints a new line, and returns nil.\n" + "If stream is specified the new line is written to the specified stream."; +const char doc170[] = "(read-byte stream)\n" + "Reads a byte from a stream and returns it."; +const char doc171[] = "(read-line [stream])\n" + "Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline.\n" + "If stream is specified the line is read from the specified stream."; +const char doc172[] = "(write-byte number [stream])\n" + "Writes a byte to a stream."; +const char doc173[] = "(write-string string [stream])\n" + "Writes a string. If stream is specified the string is written to the stream."; +const char doc174[] = "(write-line string [stream])\n" + "Writes a string terminated by a newline character. If stream is specified the string is written to the stream."; +const char doc175[] = "(restart-i2c stream [read-p])\n" + "Restarts an i2c-stream.\n" + "If read-p is nil or omitted the stream is written to.\n" + "If read-p is an integer it specifies the number of bytes to be read from the stream."; +const char doc176[] = "(gc)\n" + "Forces a garbage collection and prints the number of objects collected, and the time taken."; +const char doc177[] = "(room)\n" + "Returns the number of free Lisp cells remaining."; +const char doc180[] = "(cls)\n" + "Prints a clear-screen character."; +const char doc181[] = "(digitalread pin)\n" + "Reads the state of the specified Arduino pin number and returns t (high) or nil (low)."; +const char doc182[] = "(analogreadresolution bits)\n" + "Specifies the resolution for the analogue inputs on platforms that support it.\n" + "The default resolution on all platforms is 10 bits."; +const char doc183[] = "(analogwrite pin value)\n" + "Writes the value to the specified Arduino pin number."; +const char doc184[] = "(delay number)\n" + "Delays for a specified number of milliseconds."; +const char doc185[] = "(millis)\n" + "Returns the time in milliseconds that uLisp has been running."; +const char doc186[] = "(sleep secs)\n" + "Puts the processor into a low-power sleep mode for secs.\n" + "Only supported on some platforms. On other platforms it does delay(1000*secs)."; +const char doc187[] = "(note [pin] [note] [octave])\n" + "Generates a square wave on pin.\n" + "The argument note represents the note in the well-tempered scale, from 0 to 11,\n" + "where 0 represents C, 1 represents C#, and so on.\n" + "The argument octave can be from 3 to 6. If omitted it defaults to 0."; +const char doc188[] = "(edit 'function)\n" + "Calls the Lisp tree editor to allow you to edit a function definition."; +const char doc189[] = "(pprint item [str])\n" + "Prints its argument, using the pretty printer, to display it formatted in a structured way.\n" + "If str is specified it prints to the specified stream. It returns no value."; +const char doc190[] = "(pprintall [str])\n" + "Pretty-prints the definition of every function and variable defined in the uLisp workspace.\n" + "If str is specified it prints to the specified stream. It returns no value."; +const char doc191[] = "(require 'symbol)\n" + "Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library.\n" + "It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library."; +const char doc192[] = "(list-library)\n" + "Prints a list of the functions defined in the List Library."; +const char doc193[] = "(? item)\n" + "Prints the documentation string of a built-in or user-defined function."; +const char doc194[] = "(documentation 'symbol [type])\n" + "Returns the documentation string of a built-in or user-defined function. The type argument is ignored."; +const char doc195[] = "(apropos item)\n" + "Prints the user-defined and built-in functions whose names contain the specified string or symbol."; +const char doc196[] = "(apropos-list item)\n" + "Returns a list of user-defined and built-in functions whose names contain the specified string or symbol."; +const char doc197[] = "(unwind-protect form1 [forms]*)\n" + "Evaluates form1 and forms in order and returns the value of form1,\n" + "but guarantees to evaluate forms even if an error occurs in form1."; +const char doc198[] = "(ignore-errors [forms]*)\n" + "Evaluates forms ignoring errors."; +const char doc199[] = "(error controlstring [arguments]*)\n" + "Signals an error. The message is printed by format using the controlstring and arguments."; +const char doc200[] = "(with-client (str [address port]) form*)\n" + "Evaluates the forms with str bound to a wifi-stream."; +const char doc201[] = "(available stream)\n" + "Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available."; +const char doc202[] = "(wifi-server)\n" + "Starts a Wi-Fi server running. It returns nil."; +const char doc203[] = "(wifi-softap ssid [password channel hidden])\n" + "Set up a soft access point to establish a Wi-Fi network.\n" + "Returns the IP address as a string or nil if unsuccessful."; +const char doc204[] = "(connected stream)\n" + "Returns t or nil to indicate if the client on stream is connected."; +const char doc205[] = "(wifi-localip)\n" + "Returns the IP address of the local network as a string."; +const char doc206[] = "(wifi-connect [ssid pass])\n" + "Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string."; +const char doc207[] = "(with-gfx (str) form*)\n" + "Evaluates the forms with str bound to an gfx-stream so you can print text\n" + "to the graphics display using the standard uLisp print commands."; +const char doc208[] = "(draw-pixel x y [colour])\n" + "Draws a pixel at coordinates (x,y) in colour, or white if omitted."; +const char doc209[] = "(draw-line x0 y0 x1 y1 [colour])\n" + "Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted."; +const char doc210[] = "(draw-rect x y w h [colour])\n" + "Draws an outline rectangle with its top left corner at (x,y), with width w,\n" + "and with height h. The outline is drawn in colour, or white if omitted."; +const char doc211[] = "(fill-rect x y w h [colour])\n" + "Draws a filled rectangle with its top left corner at (x,y), with width w,\n" + "and with height h. The outline is drawn in colour, or white if omitted."; +const char doc212[] = "(draw-circle x y r [colour])\n" + "Draws an outline circle with its centre at (x, y) and with radius r.\n" + "The circle is drawn in colour, or white if omitted."; +const char doc213[] = "(fill-circle x y r [colour])\n" + "Draws a filled circle with its centre at (x, y) and with radius r.\n" + "The circle is drawn in colour, or white if omitted."; +const char doc214[] = "(draw-round-rect x y w h radius [colour])\n" + "Draws an outline rounded rectangle with its top left corner at (x,y), with width w,\n" + "height h, and corner radius radius. The outline is drawn in colour, or white if omitted."; +const char doc215[] = "(fill-round-rect x y w h radius [colour])\n" + "Draws a filled rounded rectangle with its top left corner at (x,y), with width w,\n" + "height h, and corner radius radius. The outline is drawn in colour, or white if omitted."; +const char doc216[] = "(draw-triangle x0 y0 x1 y1 x2 y2 [colour])\n" + "Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3).\n" + "The outline is drawn in colour, or white if omitted."; +const char doc217[] = "(fill-triangle x0 y0 x1 y1 x2 y2 [colour])\n" + "Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3).\n" + "The outline is drawn in colour, or white if omitted."; +const char doc218[] = "(draw-char x y char [colour background size])\n" + "Draws the character char with its top left corner at (x,y).\n" + "The character is drawn in a 5 x 7 pixel font in colour against background,\n" + "which default to white and black respectively.\n" + "The character can optionally be scaled by size."; +const char doc219[] = "(set-cursor x y)\n" + "Sets the start point for text plotting to (x, y)."; +const char doc220[] = "(set-text-color colour [background])\n" + "Sets the text colour for text plotted using (with-gfx ...)."; +const char doc221[] = "(set-text-size scale)\n" + "Scales text by the specified size, default 1."; +const char doc222[] = "(set-text-wrap boolean)\n" + "Specified whether text wraps at the right-hand edge of the display; the default is t."; +const char doc223[] = "(fill-screen [colour])\n" + "Fills or clears the screen with colour, default black."; +const char doc224[] = "(set-rotation option)\n" + "Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3."; +const char doc225[] = "(invert-display boolean)\n" + "Mirror-images the display."; + +const char doccatch[] = "(catch 'tag form*)\n" + "Evaluates the forms, and if at any point (throw) is called with the same\n" + "tag, immediately returns the \"thrown\" value from (catch). If none throw,\n" + "returns the value returned by the last form."; +const char docthrow[] = "(throw 'tag [value])\n" + "Exits the (catch) form opened with the same tag (compared using eq).\n" + "It is an error to call (throw) without first entering a (catch) with\n" + "the same tag."; + +const char docmacroexpand1[] = "(macroexpand-1 'form)\n" + "If the form represents a call to a macro, expands the macro once and returns the expanded code."; +const char docmacroexpand[] = "(macroexpand 'form)\n" + "Repeatedly applies (macroexpand-1) until the form no longer represents a call to a macro,\n" + "then returns the new form."; + +// Built-in symbol lookup table +const tbl_entry_t BuiltinTable[] = { + { string0, NULL, MINMAX(OTHER_FORMS, 0, 0), doc0 }, + { string1, NULL, MINMAX(OTHER_FORMS, 0, 0), doc1 }, + { string2, NULL, MINMAX(OTHER_FORMS, 0, 0), doc2 }, + { string3, NULL, MINMAX(OTHER_FORMS, 0, 0), doc3 }, + { stringfeatures, ss_features, MINMAX(SPECIAL_SYMBOLS, 0, 0), docfeatures }, + { string4, NULL, MINMAX(OTHER_FORMS, 0, 0), NULL }, + { string5, NULL, MINMAX(OTHER_FORMS, 0, 0), NULL }, + { stringtest, NULL, MINMAX(OTHER_FORMS, 0, 0), NULL }, + { string67, fn_eq, MINMAX(FUNCTIONS, 2, 2), doc67 }, + { string6, NULL, MINMAX(OTHER_FORMS, 0, 0), NULL }, + { string7, NULL, MINMAX(OTHER_FORMS, 0, 0), doc7 }, + { string8, NULL, MINMAX(OTHER_FORMS, 1, UNLIMITED), doc8 }, + { stringmacro, NULL, MINMAX(OTHER_FORMS, 1, UNLIMITED), docmacro }, + { string9, NULL, MINMAX(OTHER_FORMS, 1, UNLIMITED), doc9 }, + { string10, NULL, MINMAX(OTHER_FORMS, 1, UNLIMITED), doc10 }, + { string11, NULL, MINMAX(OTHER_FORMS, 1, UNLIMITED), NULL }, + { string12, NULL, MINMAX(OTHER_FORMS, 0, UNLIMITED), NULL }, + { string13, sp_quote, MINMAX(SPECIAL_FORMS, 1, 1), NULL }, + { stringbackquote, sp_backquote, MINMAX(SPECIAL_FORMS, 1, 1), docbackquote }, + { stringunquote, bq_invalid, MINMAX(SPECIAL_FORMS, 1, 1), docunquote }, + { stringuqsplicing, bq_invalid, MINMAX(SPECIAL_FORMS, 1, 1), docunquotesplicing }, + { string57, fn_cons, MINMAX(FUNCTIONS, 2, 2), doc57 }, + { string92, fn_append, MINMAX(FUNCTIONS, 0, UNLIMITED), doc92 }, + { string14, sp_defun, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), doc14 }, + { string36, sp_setf, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), doc36 }, + { string139, fn_char, MINMAX(FUNCTIONS, 2, 2), doc139 }, + { string15, sp_defvar, MINMAX(SPECIAL_FORMS, 1, 3), doc15 }, + { stringdefmacro, sp_defmacro, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), docdefmacro }, + { string16, fn_car, MINMAX(FUNCTIONS, 1, 1), doc16 }, + { string17, fn_car, MINMAX(FUNCTIONS, 1, 1), NULL }, + { string18, fn_cdr, MINMAX(FUNCTIONS, 1, 1), doc18 }, + { string19, fn_cdr, MINMAX(FUNCTIONS, 1, 1), NULL }, + { string20, fn_nth, MINMAX(FUNCTIONS, 2, 2), doc20 }, + { string21, fn_aref, MINMAX(FUNCTIONS, 2, UNLIMITED), doc21 }, + { string22, fn_stringfn, MINMAX(FUNCTIONS, 1, 1), doc22 }, + { string23, fn_pinmode, MINMAX(FUNCTIONS, 2, 2), doc23 }, + { string24, fn_digitalwrite, MINMAX(FUNCTIONS, 2, 2), doc24 }, + { string25, fn_analogread, MINMAX(FUNCTIONS, 1, 1), doc25 }, + { string26, fn_register, MINMAX(FUNCTIONS, 1, 2), doc26 }, + { string27, fn_format, MINMAX(FUNCTIONS, 2, UNLIMITED), doc27 }, + { string28, sp_or, MINMAX(SPECIAL_FORMS, 0, UNLIMITED), doc28 }, + { string29, sp_setq, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), doc29 }, + { string30, sp_loop, MINMAX(SPECIAL_FORMS, 0, UNLIMITED), doc30 }, + { string31, fn_return, MINMAX(FUNCTIONS, 0, 1), doc31 }, + { string32, sp_push, MINMAX(SPECIAL_FORMS, 2, 2), doc32 }, + { string33, sp_pop, MINMAX(SPECIAL_FORMS, 1, 1), doc33 }, + { string34, sp_incf, MINMAX(SPECIAL_FORMS, 1, 2), doc34 }, + { string35, sp_decf, MINMAX(SPECIAL_FORMS, 1, 2), doc35 }, + { string37, sp_dolist, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc37 }, + { string38, sp_dotimes, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc38 }, + { stringdo, sp_do, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), docdo }, + { stringdostar, sp_dostar, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), docdostar }, + { string39, sp_trace, MINMAX(SPECIAL_FORMS, 0, 1), doc39 }, + { string40, sp_untrace, MINMAX(SPECIAL_FORMS, 0, 1), doc40 }, + { string41, sp_formillis, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc41 }, + { string42, sp_time, MINMAX(SPECIAL_FORMS, 1, 1), doc42 }, + { string43, sp_withoutputtostring, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc43 }, + { string44, sp_withserial, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc44 }, + { string45, sp_withi2c, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc45 }, + { string46, sp_withspi, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc46 }, + { string47, sp_withsdcard, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), doc47 }, + { string48, sp_progn, MINMAX(SPECIAL_FORMS, 0, UNLIMITED), doc48 }, + { string49, sp_if, MINMAX(SPECIAL_FORMS, 2, 3), doc49 }, + { string50, sp_cond, MINMAX(SPECIAL_FORMS, 0, UNLIMITED), doc50 }, + { string51, sp_when, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc51 }, + { string52, sp_unless, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc52 }, + { string53, sp_case, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc53 }, + { string54, sp_and, MINMAX(SPECIAL_FORMS, 0, UNLIMITED), doc54 }, + { string55, fn_not, MINMAX(FUNCTIONS, 1, 1), doc55 }, + { string56, fn_not, MINMAX(FUNCTIONS, 1, 1), NULL }, + { string58, fn_atom, MINMAX(FUNCTIONS, 1, 1), doc58 }, + { string59, fn_listp, MINMAX(FUNCTIONS, 1, 1), doc59 }, + { string60, fn_consp, MINMAX(FUNCTIONS, 1, 1), doc60 }, + { string61, fn_symbolp, MINMAX(FUNCTIONS, 1, 1), doc61 }, + { string62, fn_arrayp, MINMAX(FUNCTIONS, 1, 1), doc62 }, + { string63, fn_boundp, MINMAX(FUNCTIONS, 1, 1), doc63 }, + { string64, fn_keywordp, MINMAX(FUNCTIONS, 1, 1), doc64 }, + { string65, fn_setfn, MINMAX(FUNCTIONS, 2, UNLIMITED), doc65 }, + { string66, fn_streamp, MINMAX(FUNCTIONS, 1, 1), doc66 }, + { string68, fn_equal, MINMAX(FUNCTIONS, 2, 2), doc68 }, + { string69, fn_caar, MINMAX(FUNCTIONS, 1, 1), doc69 }, + { string70, fn_cadr, MINMAX(FUNCTIONS, 1, 1), doc70 }, + { string71, fn_cadr, MINMAX(FUNCTIONS, 1, 1), NULL }, + { string72, fn_cdar, MINMAX(FUNCTIONS, 1, 1), doc72 }, + { string73, fn_cddr, MINMAX(FUNCTIONS, 1, 1), doc73 }, + { string74, fn_caaar, MINMAX(FUNCTIONS, 1, 1), doc74 }, + { string75, fn_caadr, MINMAX(FUNCTIONS, 1, 1), doc75 }, + { string76, fn_cadar, MINMAX(FUNCTIONS, 1, 1), doc76 }, + { string77, fn_caddr, MINMAX(FUNCTIONS, 1, 1), doc77 }, + { string78, fn_caddr, MINMAX(FUNCTIONS, 1, 1), NULL }, + { string79, fn_cdaar, MINMAX(FUNCTIONS, 1, 1), doc79 }, + { string80, fn_cdadr, MINMAX(FUNCTIONS, 1, 1), doc80 }, + { string81, fn_cddar, MINMAX(FUNCTIONS, 1, 1), doc81 }, + { string82, fn_cdddr, MINMAX(FUNCTIONS, 1, 1), doc82 }, + { string83, fn_length, MINMAX(FUNCTIONS, 1, 1), doc83 }, + { string84, fn_arraydimensions, MINMAX(FUNCTIONS, 1, 1), doc84 }, + { string85, fn_list, MINMAX(FUNCTIONS, 0, UNLIMITED), doc85 }, + { stringcopylist, fn_copylist, MINMAX(FUNCTIONS, 1, 1), doccopylist }, + { string86, fn_makearray, MINMAX(FUNCTIONS, 1, 5), doc86 }, + { string87, fn_reverse, MINMAX(FUNCTIONS, 1, 1), doc87 }, + { string88, fn_assoc, MINMAX(FUNCTIONS, 2, 4), doc88 }, + { string89, fn_member, MINMAX(FUNCTIONS, 2, 4), doc89 }, + { string90, fn_apply, MINMAX(FUNCTIONS, 2, UNLIMITED), doc90 }, + { string91, fn_funcall, MINMAX(FUNCTIONS, 1, UNLIMITED), doc91 }, + { string93, fn_mapc, MINMAX(FUNCTIONS, 2, UNLIMITED), doc93 }, + { stringmapl, fn_mapl, MINMAX(FUNCTIONS, 2, UNLIMITED), docmapl }, + { string94, fn_mapcar, MINMAX(FUNCTIONS, 2, UNLIMITED), doc94 }, + { string95, fn_mapcan, MINMAX(FUNCTIONS, 2, UNLIMITED), doc95 }, + { stringmaplist, fn_maplist, MINMAX(FUNCTIONS, 2, UNLIMITED), docmaplist }, + { stringmapcon, fn_mapcon, MINMAX(FUNCTIONS, 2, UNLIMITED), docmapcon }, + { string96, fn_add, MINMAX(FUNCTIONS, 0, UNLIMITED), doc96 }, + { string97, fn_subtract, MINMAX(FUNCTIONS, 1, UNLIMITED), doc97 }, + { string98, fn_multiply, MINMAX(FUNCTIONS, 0, UNLIMITED), doc98 }, + { string99, fn_divide, MINMAX(FUNCTIONS, 1, UNLIMITED), doc99 }, + { string100, fn_mod, MINMAX(FUNCTIONS, 2, 2), doc100 }, + { string101, fn_oneplus, MINMAX(FUNCTIONS, 1, 1), doc101 }, + { string102, fn_oneminus, MINMAX(FUNCTIONS, 1, 1), doc102 }, + { string103, fn_abs, MINMAX(FUNCTIONS, 1, 1), doc103 }, + { string104, fn_random, MINMAX(FUNCTIONS, 1, 1), doc104 }, + { string105, fn_maxfn, MINMAX(FUNCTIONS, 1, UNLIMITED), doc105 }, + { string106, fn_minfn, MINMAX(FUNCTIONS, 1, UNLIMITED), doc106 }, + { string107, fn_noteq, MINMAX(FUNCTIONS, 1, UNLIMITED), doc107 }, + { string108, fn_numeq, MINMAX(FUNCTIONS, 1, UNLIMITED), doc108 }, + { string109, fn_less, MINMAX(FUNCTIONS, 1, UNLIMITED), doc109 }, + { string110, fn_lesseq, MINMAX(FUNCTIONS, 1, UNLIMITED), doc110 }, + { string111, fn_greater, MINMAX(FUNCTIONS, 1, UNLIMITED), doc111 }, + { string112, fn_greatereq, MINMAX(FUNCTIONS, 1, UNLIMITED), doc112 }, + { string113, fn_plusp, MINMAX(FUNCTIONS, 1, 1), doc113 }, + { string114, fn_minusp, MINMAX(FUNCTIONS, 1, 1), doc114 }, + { string115, fn_zerop, MINMAX(FUNCTIONS, 1, 1), doc115 }, + { string116, fn_oddp, MINMAX(FUNCTIONS, 1, 1), doc116 }, + { string117, fn_evenp, MINMAX(FUNCTIONS, 1, 1), doc117 }, + { string118, fn_integerp, MINMAX(FUNCTIONS, 1, 1), doc118 }, + { string119, fn_numberp, MINMAX(FUNCTIONS, 1, 1), doc119 }, + { string120, fn_floatfn, MINMAX(FUNCTIONS, 1, 1), doc120 }, + { string121, fn_floatp, MINMAX(FUNCTIONS, 1, 1), doc121 }, + { string122, fn_sin, MINMAX(FUNCTIONS, 1, 1), doc122 }, + { string123, fn_cos, MINMAX(FUNCTIONS, 1, 1), doc123 }, + { string124, fn_tan, MINMAX(FUNCTIONS, 1, 1), doc124 }, + { string125, fn_asin, MINMAX(FUNCTIONS, 1, 1), doc125 }, + { string126, fn_acos, MINMAX(FUNCTIONS, 1, 1), doc126 }, + { string127, fn_atan, MINMAX(FUNCTIONS, 1, 2), doc127 }, + { string128, fn_sinh, MINMAX(FUNCTIONS, 1, 1), doc128 }, + { string129, fn_cosh, MINMAX(FUNCTIONS, 1, 1), doc129 }, + { string130, fn_tanh, MINMAX(FUNCTIONS, 1, 1), doc130 }, + { string131, fn_exp, MINMAX(FUNCTIONS, 1, 1), doc131 }, + { string132, fn_sqrt, MINMAX(FUNCTIONS, 1, 1), doc132 }, + { string133, fn_log, MINMAX(FUNCTIONS, 1, 2), doc133 }, + { string134, fn_expt, MINMAX(FUNCTIONS, 2, 2), doc134 }, + { string135, fn_ceiling, MINMAX(FUNCTIONS, 1, 2), doc135 }, + { string136, fn_floor, MINMAX(FUNCTIONS, 1, 2), doc136 }, + { string137, fn_truncate, MINMAX(FUNCTIONS, 1, 2), doc137 }, + { string138, fn_round, MINMAX(FUNCTIONS, 1, 2), doc138 }, + { string140, fn_charcode, MINMAX(FUNCTIONS, 1, 1), doc140 }, + { string141, fn_codechar, MINMAX(FUNCTIONS, 1, 1), doc141 }, + { string142, fn_characterp, MINMAX(FUNCTIONS, 1, 1), doc142 }, + { string143, fn_stringp, MINMAX(FUNCTIONS, 1, 1), doc143 }, + { string144, fn_stringeq, MINMAX(FUNCTIONS, 2, 2), doc144 }, + { string145, fn_stringless, MINMAX(FUNCTIONS, 2, 2), doc145 }, + { string146, fn_stringgreater, MINMAX(FUNCTIONS, 2, 2), doc146 }, + { stringstringnoteq, fn_stringnoteq, MINMAX(FUNCTIONS, 2, 2), docstringnoteq }, + { stringstringlesseq, fn_stringlesseq, MINMAX(FUNCTIONS, 2, 2), docstringlteq }, + { stringstringgteq, fn_stringgreatereq, MINMAX(FUNCTIONS, 2, 2), docstringgteq }, + { string147, fn_sort, MINMAX(FUNCTIONS, 2, 2), doc147 }, + { string148, fn_concatenate, MINMAX(FUNCTIONS, 1, UNLIMITED), doc148 }, + { string149, fn_subseq, MINMAX(FUNCTIONS, 2, 3), doc149 }, + { string150, fn_search, MINMAX(FUNCTIONS, 2, 2), doc150 }, + { string151, fn_readfromstring, MINMAX(FUNCTIONS, 1, 1), doc151 }, + { string152, fn_princtostring, MINMAX(FUNCTIONS, 1, 1), doc152 }, + { string153, fn_prin1tostring, MINMAX(FUNCTIONS, 1, 1), doc153 }, + { string154, fn_logand, MINMAX(FUNCTIONS, 0, UNLIMITED), doc154 }, + { string155, fn_logior, MINMAX(FUNCTIONS, 0, UNLIMITED), doc155 }, + { string156, fn_logxor, MINMAX(FUNCTIONS, 0, UNLIMITED), doc156 }, + { string157, fn_lognot, MINMAX(FUNCTIONS, 1, 1), doc157 }, + { string158, fn_ash, MINMAX(FUNCTIONS, 2, 2), doc158 }, + { string159, fn_logbitp, MINMAX(FUNCTIONS, 2, 2), doc159 }, + { string160, fn_eval, MINMAX(FUNCTIONS, 1, 1), doc160 }, + { string161, fn_globals, MINMAX(FUNCTIONS, 0, 0), doc161 }, + { string162, fn_locals, MINMAX(FUNCTIONS, 0, 0), doc162 }, + { string163, fn_makunbound, MINMAX(FUNCTIONS, 1, 1), doc163 }, + { string164, fn_break, MINMAX(FUNCTIONS, 0, 0), doc164 }, + { string165, fn_read, MINMAX(FUNCTIONS, 0, 1), doc165 }, + { string166, fn_prin1, MINMAX(FUNCTIONS, 1, 2), doc166 }, + { string167, fn_print, MINMAX(FUNCTIONS, 1, 2), doc167 }, + { string168, fn_princ, MINMAX(FUNCTIONS, 1, 2), doc168 }, + { string169, fn_terpri, MINMAX(FUNCTIONS, 0, 1), doc169 }, + { string170, fn_readbyte, MINMAX(FUNCTIONS, 0, 2), doc170 }, + { string171, fn_readline, MINMAX(FUNCTIONS, 0, 1), doc171 }, + { string172, fn_writebyte, MINMAX(FUNCTIONS, 1, 2), doc172 }, + { string173, fn_writestring, MINMAX(FUNCTIONS, 1, 2), doc173 }, + { string174, fn_writeline, MINMAX(FUNCTIONS, 1, 2), doc174 }, + { string175, fn_restarti2c, MINMAX(FUNCTIONS, 1, 2), doc175 }, + { string176, fn_gc, MINMAX(FUNCTIONS, 0, 0), doc176 }, + { string177, fn_room, MINMAX(FUNCTIONS, 0, 0), doc177 }, + { string180, fn_cls, MINMAX(FUNCTIONS, 0, 0), doc180 }, + { string181, fn_digitalread, MINMAX(FUNCTIONS, 1, 1), doc181 }, + { string182, fn_analogreadresolution, MINMAX(FUNCTIONS, 1, 1), doc182 }, + { string183, fn_analogwrite, MINMAX(FUNCTIONS, 2, 2), doc183 }, + { string184, fn_delay, MINMAX(FUNCTIONS, 1, 1), doc184 }, + { string185, fn_millis, MINMAX(FUNCTIONS, 0, 0), doc185 }, + { string186, fn_sleep, MINMAX(FUNCTIONS, 0, 1), doc186 }, + { string187, fn_note, MINMAX(FUNCTIONS, 0, 3), doc187 }, + { string188, fn_edit, MINMAX(FUNCTIONS, 1, 1), doc188 }, + { string189, fn_pprint, MINMAX(FUNCTIONS, 1, 2), doc189 }, + { string190, fn_pprintall, MINMAX(FUNCTIONS, 0, 1), doc190 }, + { string191, fn_require, MINMAX(FUNCTIONS, 1, 1), doc191 }, + { string192, fn_listlibrary, MINMAX(FUNCTIONS, 0, 0), doc192 }, + { string193, sp_help, MINMAX(SPECIAL_FORMS, 1, 1), doc193 }, + { string194, fn_documentation, MINMAX(FUNCTIONS, 1, 2), doc194 }, + { string195, fn_apropos, MINMAX(FUNCTIONS, 1, 1), doc195 }, + { string196, fn_aproposlist, MINMAX(FUNCTIONS, 1, 1), doc196 }, + { string197, sp_unwindprotect, MINMAX(SPECIAL_FORMS, 0, UNLIMITED), doc197 }, + { string198, sp_ignoreerrors, MINMAX(SPECIAL_FORMS, 0, UNLIMITED), doc198 }, + { string199, sp_error, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc199 }, + { string200, sp_withclient, MINMAX(SPECIAL_FORMS, 1, 2), doc200 }, + { string201, fn_available, MINMAX(FUNCTIONS, 1, 1), doc201 }, + { string202, fn_wifiserver, MINMAX(FUNCTIONS, 0, 0), doc202 }, + { string203, fn_wifisoftap, MINMAX(FUNCTIONS, 0, 4), doc203 }, + { string204, fn_connected, MINMAX(FUNCTIONS, 1, 1), doc204 }, + { string205, fn_wifilocalip, MINMAX(FUNCTIONS, 0, 0), doc205 }, + { string206, fn_wificonnect, MINMAX(FUNCTIONS, 0, 3), doc206 }, + { string207, sp_withgfx, MINMAX(SPECIAL_FORMS, 1, UNLIMITED), doc207 }, + { string208, fn_drawpixel, MINMAX(FUNCTIONS, 2, 3), doc208 }, + { string209, fn_drawline, MINMAX(FUNCTIONS, 4, 5), doc209 }, + { string210, fn_drawrect, MINMAX(FUNCTIONS, 4, 5), doc210 }, + { string211, fn_fillrect, MINMAX(FUNCTIONS, 4, 5), doc211 }, + { string212, fn_drawcircle, MINMAX(FUNCTIONS, 3, 4), doc212 }, + { string213, fn_fillcircle, MINMAX(FUNCTIONS, 3, 4), doc213 }, + { string214, fn_drawroundrect, MINMAX(FUNCTIONS, 5, 6), doc214 }, + { string215, fn_fillroundrect, MINMAX(FUNCTIONS, 5, 6), doc215 }, + { string216, fn_drawtriangle, MINMAX(FUNCTIONS, 6, 7), doc216 }, + { string217, fn_filltriangle, MINMAX(FUNCTIONS, 6, 7), doc217 }, + { string218, fn_drawchar, MINMAX(FUNCTIONS, 3, 6), doc218 }, + { string219, fn_setcursor, MINMAX(FUNCTIONS, 2, 2), doc219 }, + { string220, fn_settextcolor, MINMAX(FUNCTIONS, 1, 2), doc220 }, + { string221, fn_settextsize, MINMAX(FUNCTIONS, 1, 1), doc221 }, + { string222, fn_settextwrap, MINMAX(FUNCTIONS, 1, 1), doc222 }, + { string223, fn_fillscreen, MINMAX(FUNCTIONS, 0, 1), doc223 }, + { string224, fn_setrotation, MINMAX(FUNCTIONS, 1, 1), doc224 }, + { string225, fn_invertdisplay, MINMAX(FUNCTIONS, 1, 1), doc225 }, + { string226, (fn_ptr_type)LED_BUILTIN, 0, NULL }, + { string227, (fn_ptr_type)HIGH, DIGITALWRITE, NULL }, + { string228, (fn_ptr_type)LOW, DIGITALWRITE, NULL }, + { string229, (fn_ptr_type)INPUT, PINMODE, NULL }, + { string230, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL }, + { string231, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL }, + { string232, (fn_ptr_type)OUTPUT, PINMODE, NULL }, + { stringcatch, sp_catch, MINMAX(SPECIAL_FORMS, 2, UNLIMITED), doccatch }, + { stringthrow, fn_throw, MINMAX(FUNCTIONS, 1, 2), docthrow }, + { stringmacroexpand1, fn_macroexpand1, MINMAX(FUNCTIONS, 1, 1), docmacroexpand1 }, + { stringmacroexpand, fn_macroexpand, MINMAX(FUNCTIONS, 1, 1), docmacroexpand }, +}; + +// Metatable cross-reference functions + +void inittables() { + Metatable = (mtbl_entry_t*)calloc(1, sizeof(mtbl_entry_t)); + NumTables = 1; + Metatable[0].table = BuiltinTable; + Metatable[0].size = arraysize(BuiltinTable); +} + +#define addtable(x) __addtable(x, arraysize(x)) +void __addtable(const tbl_entry_t table[], size_t sz) { + NumTables++; + Metatable = (mtbl_entry_t*)realloc(Metatable, NumTables * sizeof(mtbl_entry_t)); + Metatable[NumTables - 1].table = table; + Metatable[NumTables - 1].size = sz; +} + +tbl_entry_t* getentry(builtin_t x) { + int t = 0; + while (x >= Metatable[t].size) { + x -= Metatable[t].size; + t++; + } + return &Metatable[t].table[x]; +} + +// Table lookup functions + +/* + lookupbuiltin - looks up a string in BuiltinTable[], and returns the index of its entry, + or ENDFUNCTIONS if no match is found +*/ +builtin_t lookupbuiltin(char* c) { + unsigned int end = 0, start; + for (int n = 0; n < NumTables; n++) { + start = end; + int entries = Metatable[n].size; + end = end + entries; + for (int i = 0; i < entries; i++) { + if (strcasecmp(c, Metatable[n].table[i].string) == 0) { + return (builtin_t)(start + i); + } + } + } + return ENDFUNCTIONS; +} + +/* + lookupfn - looks up the entry for name in BuiltinTable[], and returns the function entry point +*/ +fn_ptr_type lookupfn(builtin_t name) { + return getentry(name)->fptr; +} + +/* + getminmax - gets the minmax byte from BuiltinTable[] whose octets specify the type of function + and minimum and maximum number of arguments for name +*/ +minmax_t getminmax(builtin_t name) { + return getentry(name)->minmax; +} + +/* + checkminmax - checks that the number of arguments nargs for name is within the range specified by minmax +*/ +void checkminmax(builtin_t name, int nargs) { + if (name >= ENDFUNCTIONS) error2("internal error: not a builtin"); + minmax_t minmax = getminmax(name); + if (nargs < getminargs(minmax)) error2(toofewargs); + if (!unlimitedp(minmax) && nargs > getmaxargs(minmax)) error2(toomanyargs); +} + +/* + lookupdoc - looks up the documentation string for the built-in function name +*/ +const char* lookupdoc(builtin_t name) { + return getentry(name)->doc; +} + +/* + findsubstring - tests whether a specified substring occurs in the name of a built-in function +*/ +bool findsubstring(char* part, builtin_t name) { + return strstr(getentry(name)->string, part) != NULL; +} + +/* + testescape - tests whether the '~' escape character has been typed +*/ +void testescape() { + if (Serial.available() && Serial.read() == '~') error2("escape!"); +} + +/* + builtin_keywordp - check that obj is a built-in keyword +*/ +bool builtin_keywordp(object* obj) { + if (!(symbolp(obj) && builtinp(obj->name))) return false; + return getentry(builtin(obj->name))->string[0] == ':'; +} + +bool keywordp(object* obj) { + if (obj == nil) return false; + if (builtin_keywordp(obj)) return true; + symbol_t name = obj->name; + if ((name & 3) != 0) return false; // Packed symbols are never keywords + object* first_chunk = (object*)name; + if (!first_chunk) return false; + return (((first_chunk->chars) >> ((sizeof(int) - 1) * 8)) & 255) == ':'; +} + +// Main evaluator + +/* + eval - the main Lisp evaluator +*/ +object* eval(object* form, object* env) { + bool tailcall = false; +EVAL: + // Enough space? + if (Freespace <= WORKSPACESIZE >> 4) gc(form, env); + // Escape + if (tstflag(ESCAPE)) { + clrflag(ESCAPE); + error2("escape!"); + } + if (!tstflag(NOESC)) testescape(); + // Stack overflow check + if (abs(static_cast(StackBottom) - &tailcall) > MAX_STACK) error("C stack overflow", form); + + if (form == NULL) return nil; + + if (form->type >= NUMBER && form->type <= STRING) return form; // Literal + + if (symbolp(form)) { + if (form == tee) return form; + if (keywordp(form)) return form; // Keyword + symbol_t name = form->name; + object* pair = value(name, env); + if (pair != NULL) return cdr(pair); + pair = value(name, GlobalEnv); + if (pair != NULL) return cdr(pair); + // special symbol macro handling + else if (builtinp(name)) { + builtin_t bname = builtin(name); + uint8_t ft = fntype(getminmax(bname)); + if (ft == SPECIAL_SYMBOLS) return ((fn_ptr_type)lookupfn(bname))(NULL, env); + else if (ft == OTHER_FORMS) return form; + else return bfunction_from_symbol(form); + } + Context = NIL; + error("undefined", form); + } + // Expand macros + form = macroexpand(form, env); + + // It's a list + object* function = car(form); + object* args = cdr(form); + + if (function == NULL) error2("can't call nil"); + if (!listp(args)) error("can't evaluate a dotted pair", args); + + // List starts with a builtin special form? + if (symbolp(function) && builtinp(function->name)) { + builtin_t name = builtin(function->name); + + if ((name == LET) || (name == LETSTAR)) { + if (args == NULL) error2(noargument); + object* assigns = first(args); + if (!listp(assigns)) error(notalist, assigns); + object* forms = cdr(args); + object* newenv = env; + protect(newenv); + while (assigns != NULL) { + object* assign = car(assigns); + if (!consp(assign)) push(cons(assign, nil), newenv); + else if (cdr(assign) == NULL) push(cons(first(assign), nil), newenv); + else push(cons(first(assign), eval(second(assign), env)), newenv); + car(GCStack) = newenv; + if (name == LETSTAR) env = newenv; + assigns = cdr(assigns); + } + env = newenv; + unprotect(); + clrflag(TAILCALL); + form = sp_progn(forms, env); + if (tstflag(TAILCALL)) { + clrflag(TAILCALL); + goto EVAL; + } + return form; + } + + // MACRO does not do closures. + if (name == LAMBDA) { + if (env == NULL) return form; + object* envcopy = NULL; + while (env != NULL) { + object* pair = first(env); + if (pair != NULL) push(pair, envcopy); + env = cdr(env); + } + return cons(bsymbol(CLOSURE), cons(envcopy, args)); + } + uint8_t ft = fntype(getminmax(name)); + + if (ft == SPECIAL_FORMS) { + Context = name; + checkargs(args); + form = ((fn_ptr_type)lookupfn(name))(args, env); + if (tstflag(TAILCALL)) { + tailcall = true; + clrflag(TAILCALL); + goto EVAL; + } + return form; + } + if (ft == OTHER_FORMS) error("can't be used as a function", function); + } + + // Evaluate the parameters - result in head + object* fname = car(form); + bool old_tailcall = tailcall; + object* head = cons(eval(fname, env), NULL); + protect(head); // Don't GC the result list + object* tail = head; + form = cdr(form); + int nargs = 0; + + while (form != NULL) { + object* obj = cons(eval(car(form), env), NULL); + cdr(tail) = obj; + tail = obj; + form = cdr(form); + nargs++; + } + + function = car(head); + args = cdr(head); + + // fail early on calling a symbol + if (symbolp(function)) { + Context = NIL; + error("can't call a symbol", function); + } + if (bfunctionp(function)) { + builtin_t bname = builtin(function->name); + Context = bname; + checkminmax(bname, nargs); + object* result = ((fn_ptr_type)lookupfn(bname))(args, env); + unprotect(); + return result; + } + + if (consp(function)) { + symbol_t name = sym(NIL); + if (!listp(fname)) name = fname->name; + + if (isbuiltin(car(function), LAMBDA)) { + form = closure(old_tailcall, name, function, args, &env); + clrflag(TAILCALL); + unprotect(); + int trace = tracing(fname->name); + if (trace) { + object* result = eval(form, env); + indent((--(TraceDepth[trace - 1])) << 1, ' ', pserial); + pint(TraceDepth[trace - 1], pserial); + pserial(':'); + pserial(' '); + printobject(fname, pserial); + pfstring(" returned ", pserial); + printobject(result, pserial); + pln(pserial); + return result; + } else { + tailcall = true; + goto EVAL; + } + } + + if (isbuiltin(car(function), CLOSURE)) { + function = cdr(function); + form = closure(old_tailcall, name, function, args, &env); + unprotect(); + clrflag(TAILCALL); + tailcall = true; + goto EVAL; + } + } + error("illegal function", fname); + // unreachable + return nil; +} + +// Print functions + +/* + pserial - prints a character to the serial port +*/ +void pserial(char c) { + LastPrint = c; + if (c == '\n') Serial.write('\r'); + Serial.write(c); +} + +const char ControlCodes[] = "Null\0SOH\0STX\0ETX\0EOT\0ENQ\0ACK\0Bell\0Backspace\0Tab\0Newline\0VT\0" + "Page\0Return\0SO\0SI\0DLE\0DC1\0DC2\0DC3\0DC4\0NAK\0SYN\0ETB\0CAN\0EM\0SUB\0Escape\0FS\0GS\0RS\0US\0Space\0"; + +/* + pcharacter - prints a character to a stream, escaping special characters if PRINTREADABLY is false + If <= 32 prints character name; eg #\Space + If < 127 prints ASCII; eg #\A + Otherwise prints decimal; eg #\234 +*/ +void pcharacter(char c, pfun_t pfun) { + if (!tstflag(PRINTREADABLY)) pfun(c); + else { + pfun('#'); + pfun('\\'); + if (c <= 32) { + const char* p = ControlCodes; + while (c > 0) { + p = p + strlen_P(p) + 1; + c--; + } + pfstring(p, pfun); + } else if (c < 127) pfun(c); + else pint(c, pfun); + } +} + +/* + pstring - prints a C string to the specified stream +*/ +void pstring(char* s, pfun_t pfun) { + while (*s) pfun(*s++); +} + +/* + plispstring - prints a Lisp string object to the specified stream +*/ +void plispstring(object* form, pfun_t pfun) { + plispstr(form->name, pfun); +} + +/* + plispstr - prints a Lisp string name to the specified stream +*/ +void plispstr(symbol_t name, pfun_t pfun) { + object* form = (object*)name; + while (form != NULL) { + int chars = form->chars; + for (int i = (sizeof(int) - 1) * 8; i >= 0; i = i - 8) { + char ch = chars >> i & 0xFF; + if (tstflag(PRINTREADABLY) && (ch == '"' || ch == '\\')) pfun('\\'); + if (ch) pfun(ch); + } + form = car(form); + } +} + +/* + printstring - prints a Lisp string object to the specified stream + taking account of the PRINTREADABLY flag +*/ +void printstring(object* form, pfun_t pfun) { + if (tstflag(PRINTREADABLY)) pfun('"'); + plispstr(form->name, pfun); + if (tstflag(PRINTREADABLY)) pfun('"'); +} + +/* + pbuiltin - prints a built-in symbol to the specified stream +*/ +void pbuiltin(builtin_t name, pfun_t pfun) { + int p = 0; + const char* s = getentry(name)->string; + for (;;) { + char c = s[p++]; + if (c == 0) return; + pfun(c); + } +} + +/* + pradix40 - prints a radix 40 symbol to the specified stream +*/ +void pradix40(symbol_t name, pfun_t pfun) { + uint32_t x = untwist(name); + for (int d = 102400000; d > 0; d = d / 40) { + uint32_t j = x / d; + char c = fromradix40(j); + if (c == 0) return; + pfun(c); + x = x - j * d; + } +} + +/* + printsymbol - prints any symbol from a symbol object to the specified stream +*/ +void printsymbol(object* form, pfun_t pfun) { + psymbol(form->name, pfun); +} + +/* + psymbol - prints any symbol from a symbol name to the specified stream +*/ +void psymbol(symbol_t name, pfun_t pfun) { + if (longnamep(name)) plispstr(name, pfun); + else { + uint32_t value = untwist(name); + if (value < PACKEDS) error2("invalid symbol"); + else if (value >= BUILTINS) pbuiltin((builtin_t)(value - BUILTINS), pfun); + else pradix40(name, pfun); + } +} + +/* + pfstring - prints a string from flash memory to the specified stream +*/ +void pfstring(const char* s, pfun_t pfun) { + for (;;) { + char c = *s++; + if (c == 0) return; + pfun(c); + } +} + +/* + pint - prints an integer in decimal to the specified stream +*/ +void pint(int i, pfun_t pfun) { + uint32_t j = i; + if (i < 0) { + pfun('-'); + j = -i; + } + pintbase(j, 10, pfun); +} + +/* + pintbase - prints an integer in base 'base' to the specified stream +*/ +void pintbase(uint32_t i, uint8_t base, pfun_t pfun) { + int lead = 0; + uint32_t p = 1000000000; + if (base == 2) p = 0x80000000; + else if (base == 16) p = 0x10000000; + for (uint32_t d = p; d > 0; d = d / base) { + uint32_t j = i / d; + if (j != 0 || lead || d == 1) { + pfun((j < 10) ? j + '0' : j + 'W'); + lead = 1; + } + i = i - j * d; + } +} + +/* + pmantissa - prints the mantissa of a floating-point number to the specified stream +*/ +void pmantissa(float f, pfun_t pfun) { + int sig = floor(log10(f)); + int mul = pow(10, 5 - sig); + int i = round(f * mul); + bool point = false; + if (i == 1000000) { + i = 100000; + sig++; + } + if (sig < 0) { + pfun('0'); + pfun('.'); + point = true; + for (int j = 0; j < -sig - 1; j++) pfun('0'); + } + mul = 100000; + for (int j = 0; j < 7; j++) { + int d = (int)(i / mul); + pfun(d + '0'); + i = i - d * mul; + if (i == 0) { + if (!point) { + for (int k = j; k < sig; k++) pfun('0'); + pfun('.'); + pfun('0'); + } + return; + } + if (j == sig && sig >= 0) { + pfun('.'); + point = true; + } + mul = mul / 10; + } +} + +/* + pfloat - prints a floating-point number to the specified stream +*/ +void pfloat(float f, pfun_t pfun) { + if (isnan(f)) { + pfstring("NaN", pfun); + return; + } + if (f == 0.0) { + pfun('0'); + return; + } + if (isinf(f)) { + pfstring("Inf", pfun); + return; + } + if (f < 0) { + pfun('-'); + f = -f; + } + // Calculate exponent + int e = 0; + if (f < 1e-3 || f >= 1e5) { + e = floor(log(f) / 2.302585); // log10 gives wrong result + f = f / pow(10, e); + } + + pmantissa(f, pfun); + + // Exponent + if (e != 0) { + pfun('e'); + pint(e, pfun); + } +} + +/* + pln - prints a newline to the specified stream +*/ +inline void pln(pfun_t pfun) { + pfun('\n'); +} + +/* + pfl - prints a newline to the specified stream if a newline has not just been printed +*/ +void pfl(pfun_t pfun) { + if (LastPrint != '\n') pfun('\n'); +} + +/* + plist - prints a list to the specified stream +*/ +void plist(object* form, pfun_t pfun) { + pfun('('); + printobject(car(form), pfun); + form = cdr(form); + while (form != NULL && listp(form)) { + pfun(' '); + printobject(car(form), pfun); + form = cdr(form); + } + if (form != NULL) { + pfstring(" . ", pfun); + printobject(form, pfun); + } + pfun(')'); +} + +/* + pstream - prints a stream name to the specified stream +*/ +void pstream(object* form, pfun_t pfun) { + pfun('<'); + pfstring(streamname[(form->integer) >> 8], pfun); + pfstring("-stream ", pfun); + pint(form->integer & 0xFF, pfun); + pfun('>'); +} + +/* + printobject - prints any Lisp object to the specified stream +*/ +void printobject(object* form, pfun_t pfun) { + if (form == NULL) pfstring("nil", pfun); + else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring("", pfun); + else if (listp(form)) plist(form, pfun); + else if (integerp(form)) pint(form->integer, pfun); + else if (floatp(form)) pfloat(form->single_float, pfun); + else if (symbolp(form)) { + if (form->name != sym(NOTHING)) printsymbol(form, pfun); + } else if (bfunctionp(form)) { + pfstring("name)))) { + case FUNCTIONS: pfstring("function ", pfun); break; + case SPECIAL_FORMS: pfstring("special form ", pfun); break; + } + printsymbol(form, pfun); + pfun('>'); + } else if (characterp(form)) pcharacter(form->chars, pfun); + else if (stringp(form)) printstring(form, pfun); + else if (arrayp(form)) printarray(form, pfun); + else if (streamp(form)) pstream(form, pfun); + else error2("internal error in print"); +} + +/* + prin1object - prints any Lisp object to the specified stream escaping special characters +*/ +void prin1object(object* form, pfun_t pfun) { + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printobject(form, pfun); + Flags = temp; +} + +// Read functions + +/* + glibrary - reads a character from the Lisp Library +*/ +int glibrary() { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + char c = LispLibrary[GlobalStringIndex++]; + return (c != 0) ? c : -1; // -1? +} + +/* + loadfromlibrary - reads and evaluates a form from the Lisp Library +*/ +void loadfromlibrary(object* env) { + GlobalStringIndex = 0; + object* line = read(glibrary); + while (line != NULL) { + protect(line); + eval(line, env); + unprotect(); + line = read(glibrary); + } +} + +/* + gserial - gets a character from the serial port +*/ +int gserial() { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + unsigned long start = millis(); + while (!Serial.available()) { + delay(1); + if (millis() - start > 1000) clrflag(NOECHO); + } + char temp = Serial.read(); + if (temp != '\n' && !tstflag(NOECHO)) pserial(temp); + return temp; +} + +/* + nextitem - reads the next token from the specified stream +*/ +object* nextitem(gfun_t gfun) { + int ch = gfun(); + while (issp(ch)) ch = gfun(); + + if (ch == ';') { + do { + ch = gfun(); + if (ch == ';' || ch == '(') setflag(NOECHO); + } while (ch != '('); + } + if (ch == '\n') ch = gfun(); + if (ch == -1) return nil; + if (ch == ')') return (object*)CLOSE_PAREN; + if (ch == '(') return (object*)OPEN_PAREN; + if (ch == '\'') return (object*)SINGLE_QUOTE; + if (ch == '`') return (object*)BACKTICK; + if (ch == '@') return (object*)COMMA_AT; // maintain compatibility with old Dave Astels code + if (ch == ',') { + ch = gfun(); + if (ch == '@') return (object*)COMMA_AT; + else { + LastChar = ch; + return (object*)COMMA; + } + } + + // Parse string + if (ch == '"') return readstring('"', true, gfun); + + // Parse symbol, character, or number + int index = 0, base = 10, sign = 1; + char buffer[BUFFERSIZE]; + int bufmax = BUFFERSIZE - 3; // Max index + unsigned int result = 0; + bool isfloat = false; + float fresult = 0.0; + + if (ch == '+') { + buffer[index++] = ch; + ch = gfun(); + } else if (ch == '-') { + sign = -1; + buffer[index++] = ch; + ch = gfun(); + } else if (ch == '.') { + buffer[index++] = ch; + ch = gfun(); + if (ch == ' ') return (object*)PERIOD; + isfloat = true; + } + + // Parse reader macros + else if (ch == '#') { + ch = gfun(); + char ch2 = ch & ~0x20; // force to upper case + if (ch == '\\') { // Character + base = 0; + ch = gfun(); + if (issp(ch) || isbr(ch)) return character(ch); + else LastChar = ch; + } else if (ch == '|') { + do { + while (gfun() != '|') + ; + } while (gfun() != '#'); + return nextitem(gfun); + } else if (ch2 == 'B') base = 2; + else if (ch2 == 'O') base = 8; + else if (ch2 == 'X') base = 16; + else if (ch == '\'') return nextitem(gfun); + else if (ch == '.') { + setflag(NOESC); + object* result = eval(read(gfun), NULL); + clrflag(NOESC); + return result; + } else if (ch == '(') { + LastChar = ch; + return readarray(1, read(gfun)); + } else if (ch == '*') return readbitarray(gfun); + else if (ch >= '1' && ch <= '9' && (gfun() & ~0x20) == 'A') return readarray(ch - '0', read(gfun)); + else error2("illegal character after #"); + ch = gfun(); + } + int valid; // 0=undecided, -1=invalid, +1=valid + if (ch == '.') valid = 0; + else if (digitvalue(ch) < base) valid = 1; + else valid = -1; + bool isexponent = false; + int exponent = 0, esign = 1; + buffer[2] = '\0'; + buffer[3] = '\0'; + buffer[4] = '\0'; + buffer[5] = '\0'; // In case symbol is < 5 letters + float divisor = 10.0; + + while (!issp(ch) && !isbr(ch) && index < bufmax) { + buffer[index++] = ch; + if (base == 10 && ch == '.' && !isexponent) { + isfloat = true; + fresult = result; + } else if (base == 10 && (ch == 'e' || ch == 'E')) { + if (!isfloat) { + isfloat = true; + fresult = result; + } + isexponent = true; + if (valid == 1) valid = 0; + else valid = -1; + } else if (isexponent && ch == '-') { + esign = -esign; + } else if (isexponent && ch == '+') { + } else { + int digit = digitvalue(ch); + if (digitvalue(ch) < base && valid != -1) valid = 1; + else valid = -1; + if (isexponent) { + exponent = exponent * 10 + digit; + } else if (isfloat) { + fresult = fresult + digit / divisor; + divisor = divisor * 10.0; + } else { + result = result * base + digit; + } + } + ch = gfun(); + } + + buffer[index] = '\0'; + if (isbr(ch)) LastChar = ch; + if (isfloat && valid == 1) return makefloat(fresult * sign * pow(10, exponent * esign)); + else if (valid == 1) { + if (base == 10 && result > ((unsigned int)INT_MAX + (1 - sign) / 2)) + return makefloat((float)result * sign); + return number(result * sign); + } else if (base == 0) { + if (index == 1) return character(buffer[0]); + const char* p = ControlCodes; + char c = 0; + while (c < 33) { + if (strcasecmp_P(buffer, p) == 0) return character(c); + p = p + strlen_P(p) + 1; + c++; + } + if (index == 3) return character((buffer[0] * 10 + buffer[1]) * 10 + buffer[2] - 5328); + error2("unknown character"); + } + + builtin_t x = lookupbuiltin(buffer); + if (x == NIL) return nil; + if (x != ENDFUNCTIONS) return bsymbol(x); + return buftosymbol(buffer); +} + +/* + readrest - reads the remaining tokens from the specified stream +*/ +object* readrest(gfun_t gfun) { + object* item = nextitem(gfun); + object* head = NULL; + object* tail = NULL; + + while (item != (object*)CLOSE_PAREN) { + if (item == (object*)OPEN_PAREN) item = readrest(gfun); + else if (item == (object*)SINGLE_QUOTE) item = quoteit(QUOTE, read(gfun)); + else if (item == (object*)BACKTICK) item = quoteit(BACKQUOTE, read(gfun)); + else if (item == (object*)COMMA) item = quoteit(UNQUOTE, read(gfun)); + else if (item == (object*)COMMA_AT) item = quoteit(UNQUOTE_SPLICING, read(gfun)); + else if (item == (object*)PERIOD) { + tail->cdr = read(gfun); + if (readrest(gfun) != NULL) error2("only one form allowed after reader dot"); + return head; + } else { + object* cell = cons(item, NULL); + if (head == NULL) head = cell; + else tail->cdr = cell; + tail = cell; + item = nextitem(gfun); + } + } + return head; +} + +/* + read - recursively reads a Lisp object from the stream gfun and returns it +*/ +object* read(gfun_t gfun) { + object* item = nextitem(gfun); + if (item == (object*)CLOSE_PAREN) error2("unexpected close paren"); + if (item == (object*)OPEN_PAREN) return readrest(gfun); + if (item == (object*)PERIOD) return read(gfun); + if (item == (object*)SINGLE_QUOTE) return quoteit(QUOTE, read(gfun)); + if (item == (object*)BACKTICK) return quoteit(BACKQUOTE, read(gfun)); + if (item == (object*)COMMA) return quoteit(UNQUOTE, read(gfun)); + if (item == (object*)COMMA_AT) return quoteit(UNQUOTE_SPLICING, read(gfun)); + return item; +} + +// Setup + +/* + initenv - initialises the uLisp environment +*/ +void initenv() { + GlobalEnv = NULL; + tee = bsymbol(TEE); +} + +/* + initgfx - initialises the graphics +*/ +void initgfx() { +#if defined(gfxsupport) + tft.init(135, 240); + tft.setRotation(1); + tft.fillScreen(ST77XX_BLACK); + pinMode(TFT_BACKLITE, OUTPUT); + digitalWrite(TFT_BACKLITE, HIGH); +#endif +} + +void ulispinit() { + int foo = 0; + StackBottom = &foo; + initworkspace(); + inittables(); + initenv(); + initsleep(); + initgfx(); +} + +// Read/Evaluate/Print loop + +/* + repl - the Lisp Read/Evaluate/Print loop +*/ +void repl(object* env) { + for (;;) { + randomSeed(micros()); + gc(NULL, env); + if (BreakLevel) { + pfstring(" : ", pserial); + pint(BreakLevel, pserial); + } + pfstring("[Ready.]\n", pserial); + pint(Freespace, pserial); + pserial('/'); + pint(WORKSPACESIZE, pserial); + pfstring("> ", pserial); + Context = NIL; + object* line = read(gserial); + if (BreakLevel && line == nil) { + pln(pserial); + return; + } + if (line == (object*)CLOSE_PAREN) error2("unmatched right bracket"); + protect(line); + pfl(pserial); + line = eval(line, env); + pfl(pserial); + pfstring("\n=> ", pserial); + printobject(line, pserial); + unprotect(); + pfl(pserial); + pln(pserial); + } +} + +void ulisperrcleanup() { + // Come here after error + delay(100); + while (Serial.available()) Serial.read(); + clrflag(NOESC); + BreakLevel = 0; + for (int i = 0; i < TRACEMAX; i++) TraceDepth[i] = 0; +#if defined(sdcardsupport) + SDpfile.close(); + SDgfile.close(); +#endif +#if defined(lisplibrary) + if (!tstflag(LIBRARYLOADED)) { + setflag(LIBRARYLOADED); + loadfromlibrary(NULL); + } +#endif + client.stop(); +} + +#endif