bf-aheago.lisp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. ;; —————————————————————————————————————
  2. ;; PACKAGE
  3. (defpackage :bf-aheago
  4. (:use :cl :anaphora)
  5. (:export :interpret)
  6. (:nicknames :bf-a))
  7. (in-package :bf-aheago)
  8. ;; —————————————————————————————————————
  9. ;; MACROS
  10. (defmacro interpret-char (char)
  11. `(progn
  12. (when debug-p (format *error-output* "~A" ,char))
  13. (cond
  14. ((eq ,char #\.) (output-cell tape pointer))
  15. ((eq ,char #\,) (input-cell tape pointer))
  16. ((eq ,char #\<) (bound-decf pointer tape-length))
  17. ((eq ,char #\>) (bound-incf pointer tape-length))
  18. ((eq ,char #\+) (inc-cell tape pointer))
  19. ((eq ,char #\-) (dec-cell tape pointer))
  20. ((eq ,char #\[) (loop-advance tape pointer input-stream))
  21. ((eq ,char #\]) (loop-rewind tape pointer input-stream)))))
  22. ;; VARYING [NUMBER] [NUMBER] → NUMBER
  23. (defmacro bound-incf (object &optional (max 256) (min 0))
  24. "Increment (destructive) an object, but bounds-check with #'bound-ensure."
  25. `(setf ,object (bound-ensure (1+ ,object) ,max ,min)))
  26. ;; VARYING [NUMBER] [NUMBER] → NUMBER
  27. (defmacro bound-decf (object &optional (max 256) (min 0))
  28. "Decrement (destructive) an object, but bounds-check with #'bound-ensure."
  29. `(setf ,object (bound-ensure (1- ,object) ,max ,min)))
  30. ;; —————————————————————————————————————
  31. ;; INTERPRETER
  32. ;; STREAM → ARRAY
  33. (defmethod interpret ((input-stream stream) &key (tape-length 30000) (debug-p nil))
  34. "Interpret the brainfuck code within the given stream: returns the tape."
  35. (let ((tape (make-tape tape-length))
  36. (pointer 0))
  37. (loop :if (not (listen input-stream))
  38. :return tape
  39. :do (alet (read-char input-stream)
  40. (interpret-char it)))))
  41. (defmethod interpret ((string string) &key (tape-length 30000) (debug-p nil))
  42. (interpret (make-input-string string) :tape-length tape-length :debug-p debug-p))
  43. (defmethod interpret ((pathname pathname) &key (tape-length 30000) (debug-p nil))
  44. (with-open-file (stream pathname)
  45. (interpret stream :tape-length tape-length :debug-p debug-p)))
  46. ;; —————————————————————————————————————
  47. ;; CELLS
  48. ;; ARRAY NUMBER → NUMBER
  49. (defun inc-cell (tape index)
  50. "Increment the given cell."
  51. (bound-incf (aref tape index)))
  52. ; (setf (aref tape index) (bound-ensure (1+ (aref tape index)))))
  53. ;; ARRAY NUMBER → NUMBENR
  54. (defun dec-cell (tape index)
  55. "Decrement the given cell."
  56. (bound-decf (aref tape index)))
  57. ; (setf (aref tape index) (bound-ensure (1- (aref tape index)))))
  58. ;; ARRAY NUMBER → NIL
  59. (defun output-cell (tape index)
  60. "Print the given cell in the tape to stdout."
  61. (format t "~A" (code-char (aref tape index))))
  62. ;; ARRAY NUMBER → CHAR
  63. (defun input-cell (tape index)
  64. "Input a char's int into the tape at given index."
  65. (alet (read-char *standard-input* nil 0)
  66. (setf (aref tape index) (if (numberp it) it (char-code it)))))
  67. ;; [NUMBER] → ARRAY
  68. (defun make-tape (&optional (length 30000))
  69. "Make a clean, 0-initialized BF tape."
  70. (make-array (list length) :initial-element 0))
  71. ;; —————————————————————————————————————
  72. ;; LOOPING []
  73. ;; ARRAY NUMBER STREAM → NIL
  74. (defun loop-rewind (tape index stream)
  75. "Restart the loop (move pointer to last '[') if nonzero cell value."
  76. (if (not (zerop (aref tape index)))
  77. (stream-rewind-to stream #\[)))
  78. ;; ARRAY NUMBER STREAM → NIL
  79. (defun loop-advance (tape index stream)
  80. "Skip the loop (move to next ']') if cell value is zero."
  81. (if (zerop (aref tape index))
  82. (stream-advance-to stream #\])))
  83. ;; —————————————————————————————————————
  84. ;; STREAM MANIP
  85. ;; STREAM → CHAR
  86. (defun retroread-char (stream)
  87. "Read the previous character in a file-stream."
  88. (alet (file-position stream)
  89. (file-position stream (- it 2)))
  90. (read-char stream))
  91. ;; STREAM CHAR → NIL
  92. (defun stream-advance-to (stream char)
  93. "Advance a stream's pointer until the given character is read."
  94. (if (not (eq char (read-char stream)))
  95. (stream-advance-to stream char)))
  96. ;; STREAM CHAR → NIL
  97. (defun stream-rewind-to (stream char)
  98. "Reverse a stream's pointer until the given character is read."
  99. (if (not (eq char (retroread-char stream)))
  100. (stream-rewind-to stream char)))
  101. ;; —————————————————————————————————————
  102. ;; MISC
  103. ;; NUMBER [NUMBER] [NUMBER]
  104. (defun bound-ensure (number &optional (max 256) (min 0))
  105. "Ensure the given number remains within the given bounds (with overflow)."
  106. (cond ((> min number) (bound-ensure (+ number max) max min))
  107. ((< max number) (bound-ensure (- max number) max min))
  108. (T number)))