Advertisement
Guest User

brainfuck interpreter

a guest
Sep 8th, 2018
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 8.30 KB | None | 0 0
  1. ;utility
  2. (defun file-get-contents (filename)
  3.   "http://sodaware.sdf.org/notes/cl-read-file-into-string/"
  4.   (with-open-file (stream filename)
  5.     (let ((contents (make-string (file-length stream))))
  6.       (read-sequence contents stream)
  7.       contents)))
  8.  
  9. ;program
  10. (load "~/quicklisp/setup.lisp")
  11. (ql:quickload "pathnames" :silent t) ;This is Peter Seibel's pathnames library from Practical Common Lisp
  12.  
  13. (defparameter +max-cell-value+ (1- (expt 2 8)))
  14. (defparameter +min-cell-value+ 0)
  15. (defparameter +instruction-list+ (list #\+ #\- #\> #\< #\. #\, #\[ #\]))
  16.  
  17. (defclass tape-memory ()
  18.   ((leftern-tape
  19.     :accessor left-tape
  20.     :initform (make-array 30 :fill-pointer 0 :adjustable t)
  21.     :initarg :leftern-tape)
  22.    (rightern-tape
  23.     :accessor right-tape
  24.     :initform (make-array 30 :fill-pointer 0 :adjustable t)
  25.     :initarg :rightern-tape))
  26.   (:documentation "A simple holder for the tape-mems to avoid loose vars."))
  27.  
  28. (defparameter *output-mode* 'ascii
  29.   "Specifies dispatch for formatting output to correct output mode.")
  30. (defparameter *input* (make-string-input-stream ""))
  31. (defparameter *PC* 0
  32.   "The Program Counter for the brainfuck programs.")
  33. (defparameter *head* 0
  34.   "The tape head/pointer aka the pointer to current memory cell.")
  35. (defparameter *program* (make-array 30 :fill-pointer 0 :adjustable t)
  36.   "Holds the actual program as a vector of instruction symbols")
  37. (defparameter *tape* (make-instance 'tape-memory)
  38.   "The infinite tape memory itself")
  39. (vector-push-extend 0 (right-tape *tape*)) ;Hacky. *head* starts at 0.
  40.  
  41. (defun left-cell-num (head)
  42.   "Convert a tape-index into appropriate left-tape vector index. 1- since
  43.   0 is treated as positive (and therefore is in right-tape."
  44.   (1- (abs head)))
  45. (defun tape-cell (tape head)
  46.   "Should return the tape-cell requested, whether that's in left or right."
  47.   (if (< head 0)
  48.       (elt (left-tape tape) (left-cell-num head))
  49.       (elt (right-tape tape) head)))
  50. (defun set-tape-cell (tape head value)
  51.   "Sets the value of the current cell to value."
  52.   (if (< head 0)
  53.        (setf (elt (left-tape tape) (left-cell-num head)) value)
  54.        (setf (elt (right-tape tape) head) value)))
  55.  
  56. (defun potential-right-extend (tape head)
  57.   "Extend the infinite right-tape if head points out-of-bounds."
  58.   (if (and (not (minusp head)) ;Want to treat 0 as positive
  59.        (>= head (fill-pointer (right-tape tape))))
  60.     (vector-push-extend 0 (right-tape tape))
  61.     'no-extension-necessary))
  62. (defun potential-left-extend (tape head)
  63.   "Extend the infinite left-tape if head points out-of-bounds."
  64.   (if (and (minusp head)
  65.        (>= (left-cell-num head) (fill-pointer (left-tape tape))))
  66.     (vector-push-extend 0 (left-tape tape))
  67.     'no-extension-necessary))
  68.  
  69. (defun increment-cell (tape head)
  70.   "Increment current cell. Wrap around to +min-cell-value+ if overflow."
  71.   (let* ((old-val (tape-cell tape head))
  72.      (new-val (if (> (1+ old-val) +max-cell-value+)
  73.               +min-cell-value+
  74.               (1+ old-val))))
  75.     (set-tape-cell tape head new-val)))
  76. (defun decrement-cell (tape head)
  77.   "Decrement current cell. Wrap around to +max-cell-value+ if underflow."
  78.   (let* ((old-val (tape-cell tape head))
  79.      (new-val (if (< (1- old-val) +min-cell-value+)
  80.               +max-cell-value+
  81.               (1- old-val))))
  82.     (set-tape-cell tape head new-val)))
  83.  
  84. (defun output-cell (tape head)
  85.   "Outputs the value of the current cell."
  86.   (let ((print-datum (output-mode-format (tape-cell tape head))))
  87.     (format *query-io* "~A" print-datum)
  88.     print-datum))
  89. (defun output-mode-format (cell-value)
  90.   "Interprets cell-value according to current *output-mode*."
  91.   (cond ((equal *output-mode* 'ascii)
  92.      (code-char cell-value))
  93.     (t (error "~A is not a valid *output-mode*." *output-mode*))))
  94.  
  95. (defun input-cell (tape head)
  96.   "Replaces the value of the current cell with the first byte of *input*, or 0 if end-of-input."
  97.   (let ((input-datum (if (listen *input*) (char-code (read-char *input*)) 0)))
  98.     (set-tape-cell tape head input-datum)))
  99.  
  100. (defun string->program (program-string)
  101.   "Turns a string into a program vector."
  102.   (let ((program-list
  103.      (remove-if #'(lambda (c) (not (member c +instruction-list+)))
  104.             (loop for c across program-string collect c)))
  105.     (program-vector (make-array 30 :fill-pointer 0 :adjustable t)))
  106.     (loop for c in program-list do (vector-push-extend c program-vector))
  107.     program-vector))
  108. (defun load-program-from-string (program-string)
  109.   "\"Loads\" a program from a string."
  110.   (setf *program* (string->program program-string)))
  111. (defun load-program-from-file (pot-file-string)
  112.   (let ((pot-file-input (com.gigamonkeys.pathnames:file-exists-p pot-file-string)))
  113.     (if pot-file-input
  114.     (load-program-from-string (file-get-contents pot-file-input))
  115.     (load-program-from-string pot-file-string))))
  116.  
  117. (defun find-matching-] (pc program)
  118.   "Linear search until match found. Error if no match."
  119.   (loop for i from (1+ pc) below (length program)
  120.        with depth = 0
  121.        when (and (zerop depth) (equal (elt program i) #\]))
  122.        do (return i)
  123.        when (equal (elt program i) #\[)
  124.        do (incf depth)
  125.        when (equal (elt program i) #\])
  126.        do (decf depth)
  127.        finally (error "~Dth instruction: [. Looked for matching ], but end-of-program with depth ~D.~%" pc depth)))
  128. (defun jump-if-zero (head tape pc program)
  129.   "Returns value so brainfuck-interpreter can update pc as required."
  130.   (if (zerop (tape-cell tape head))
  131.       (find-matching-] pc program)
  132.       pc))
  133.  
  134. (defun find-matching-[ (pc program)
  135.   "Linear search backwards until match found. Error if no match."
  136.   (loop for i from (1- pc) downto 0
  137.        with depth = 0
  138.        when (and (zerop depth) (equal (elt program i) #\[))
  139.        do (return i)
  140.        when (equal (elt program i) #\])
  141.        do (incf depth)
  142.        when (equal (elt program i) #\[)
  143.        do (decf depth)
  144.        finally (error "~Dth instruction: ]. Looked for matching [, but start-of-program with depth ~D.~%" pc depth)))
  145. (defun jump-unless-zero (head tape pc program)
  146.   "Returns value so brainfuck-interpreter can update pc as required."
  147.   (if (not (zerop (tape-cell tape head)))
  148.       (find-matching-[ pc program)
  149.       pc))
  150.  
  151. (defun move-rightp (s) (equal #\> s))
  152. (defun move-leftp (s) (equal #\< s))
  153. (defun incrementp (s) (equal #\+ s))
  154. (defun decrementp (s) (equal #\- s))
  155. (defun outputp (s) (equal #\. s))
  156. (defun inputp (s) (equal #\, s))
  157. (defun jump-if-zerop (s) (equal #\[ s))
  158. (defun jump-unless-zerop (s) (equal #\] s))
  159.  
  160. (defun brainfuck-interpret (instruction)
  161.   "The evaluator itself."
  162.   (cond ((move-rightp instruction)
  163.      (potential-right-extend *tape* (incf *head*)))
  164.     ((move-leftp instruction)
  165.      (potential-left-extend *tape* (decf *head*)))
  166.     ((incrementp instruction)
  167.      (increment-cell *tape* *head*))
  168.     ((decrementp instruction)
  169.      (decrement-cell *tape* *head*))
  170.     ((outputp instruction)
  171.      (output-cell *tape* *head*))
  172.     ((inputp instruction)
  173.      (input-cell *tape* *head*))
  174.     ((jump-if-zerop instruction)
  175.      (setf *pc* (jump-if-zero *head* *tape* *pc* *program*)))
  176.     ((jump-unless-zerop instruction)
  177.      (setf *pc* (jump-unless-zero *head* *tape* *pc* *program*)))
  178.     (t 'ignore)))
  179.  
  180. (defun input-stream-potentially-file (input)
  181.   (if (not input)
  182.       nil
  183.       (let ((pot-file-input
  184.          (com.gigamonkeys.pathnames:file-exists-p input)))
  185.     (if pot-file-input
  186.         (open pot-file-input :direction :input)
  187.         (make-string-input-stream input)))))
  188.  
  189. (defun execute (program &key (input nil))
  190.   (setf *pc* 0)
  191.   (setf *tape* (make-instance 'tape-memory))
  192.   (vector-push-extend 0 (right-tape *tape*))
  193.   (setf *head* 0)
  194.   (setf *program* program)
  195.   (setf *input* (input-stream-potentially-file input))
  196.   (loop while (< *pc* (length *program*))
  197.      do (brainfuck-interpret (elt *program* *pc*))
  198.      do (incf *pc*)))
  199.  
  200. (defun main (argv)
  201.   (let ((-program (member "-program" argv :test #'equal))
  202.     (-input (member "-input" argv :test #'equal)))
  203.     (cond ((and -program -input (= 5 (length argv)))
  204.        (execute (load-program-from-file (cadr -program))
  205.             :input (cadr -input)))
  206.       ((and -program (= 3 (length argv)))
  207.        (execute (load-program-from-file (cadr -program))))
  208.       (t (error "Malformed launch: PROG -program PATH/STRING -input PATH/STRING")))))
  209.  
  210. ;Presumably, this will only trigger when called with args from terminal:
  211. (when (< 1 (length *posix-argv*))
  212.   (main *posix-argv*))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement