123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- ;; —————————————————————————————————————
- ;; PACKAGE
- (defpackage :bf-aheago
- (:use :cl :anaphora)
- (:export :interpret)
- (:nicknames :bf-a))
- (in-package :bf-aheago)
- ;; —————————————————————————————————————
- ;; MACROS
- (defmacro interpret-char (char)
- `(progn
- (when debug-p (format *error-output* "~A" ,char))
- (cond
- ((eq ,char #\.) (output-cell tape pointer))
- ((eq ,char #\,) (input-cell tape pointer))
- ((eq ,char #\<) (bound-decf pointer tape-length))
- ((eq ,char #\>) (bound-incf pointer tape-length))
- ((eq ,char #\+) (inc-cell tape pointer))
- ((eq ,char #\-) (dec-cell tape pointer))
- ((eq ,char #\[) (loop-advance tape pointer input-stream))
- ((eq ,char #\]) (loop-rewind tape pointer input-stream)))))
- ;; VARYING [NUMBER] [NUMBER] → NUMBER
- (defmacro bound-incf (object &optional (max 256) (min 0))
- "Increment (destructive) an object, but bounds-check with #'bound-ensure."
- `(setf ,object (bound-ensure (1+ ,object) ,max ,min)))
- ;; VARYING [NUMBER] [NUMBER] → NUMBER
- (defmacro bound-decf (object &optional (max 256) (min 0))
- "Decrement (destructive) an object, but bounds-check with #'bound-ensure."
- `(setf ,object (bound-ensure (1- ,object) ,max ,min)))
- ;; —————————————————————————————————————
- ;; INTERPRETER
- ;; STREAM → ARRAY
- (defmethod interpret ((input-stream stream) &key (tape-length 30000) (debug-p nil))
- "Interpret the brainfuck code within the given stream: returns the tape."
- (let ((tape (make-tape tape-length))
- (pointer 0))
- (loop :if (not (listen input-stream))
- :return tape
- :do (alet (read-char input-stream)
- (interpret-char it)))))
- (defmethod interpret ((string string) &key (tape-length 30000) (debug-p nil))
- (interpret (make-input-string string) :tape-length tape-length :debug-p debug-p))
- (defmethod interpret ((pathname pathname) &key (tape-length 30000) (debug-p nil))
- (with-open-file (stream pathname)
- (interpret stream :tape-length tape-length :debug-p debug-p)))
- ;; —————————————————————————————————————
- ;; CELLS
- ;; ARRAY NUMBER → NUMBER
- (defun inc-cell (tape index)
- "Increment the given cell."
- (bound-incf (aref tape index)))
- ; (setf (aref tape index) (bound-ensure (1+ (aref tape index)))))
- ;; ARRAY NUMBER → NUMBENR
- (defun dec-cell (tape index)
- "Decrement the given cell."
- (bound-decf (aref tape index)))
- ; (setf (aref tape index) (bound-ensure (1- (aref tape index)))))
- ;; ARRAY NUMBER → NIL
- (defun output-cell (tape index)
- "Print the given cell in the tape to stdout."
- (format t "~A" (code-char (aref tape index))))
- ;; ARRAY NUMBER → CHAR
- (defun input-cell (tape index)
- "Input a char's int into the tape at given index."
- (alet (read-char *standard-input* nil 0)
- (setf (aref tape index) (if (numberp it) it (char-code it)))))
- ;; [NUMBER] → ARRAY
- (defun make-tape (&optional (length 30000))
- "Make a clean, 0-initialized BF tape."
- (make-array (list length) :initial-element 0))
- ;; —————————————————————————————————————
- ;; LOOPING []
- ;; ARRAY NUMBER STREAM → NIL
- (defun loop-rewind (tape index stream)
- "Restart the loop (move pointer to last '[') if nonzero cell value."
- (if (not (zerop (aref tape index)))
- (stream-rewind-to stream #\[)))
- ;; ARRAY NUMBER STREAM → NIL
- (defun loop-advance (tape index stream)
- "Skip the loop (move to next ']') if cell value is zero."
- (if (zerop (aref tape index))
- (stream-advance-to stream #\])))
- ;; —————————————————————————————————————
- ;; STREAM MANIP
- ;; STREAM → CHAR
- (defun retroread-char (stream)
- "Read the previous character in a file-stream."
- (alet (file-position stream)
- (file-position stream (- it 2)))
- (read-char stream))
- ;; STREAM CHAR → NIL
- (defun stream-advance-to (stream char)
- "Advance a stream's pointer until the given character is read."
- (if (not (eq char (read-char stream)))
- (stream-advance-to stream char)))
- ;; STREAM CHAR → NIL
- (defun stream-rewind-to (stream char)
- "Reverse a stream's pointer until the given character is read."
- (if (not (eq char (retroread-char stream)))
- (stream-rewind-to stream char)))
- ;; —————————————————————————————————————
- ;; MISC
- ;; NUMBER [NUMBER] [NUMBER]
- (defun bound-ensure (number &optional (max 256) (min 0))
- "Ensure the given number remains within the given bounds (with overflow)."
- (cond ((> min number) (bound-ensure (+ number max) max min))
- ((< max number) (bound-ensure (- max number) max min))
- (T number)))
|