Brainf ** k在Common Lisp中实现

时间:2017-01-31 17:48:36

标签: common-lisp brainfuck

我尝试在Common Lisp,SBCL中实现Brainfuck。我遇到了一些问题。

(defparameter *tape* (make-array '(1) :adjustable t))
(defparameter *pointer* 0)
(defparameter *tape-size* 1)
(defparameter *output* (make-array '(0) :element-type 'base-char :fill-pointer 0 :adjustable t))

(defun move-pointer-right (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(progn 
  (incf *tape-size*)
  (adjust-array *tape* (list *tape-size*))
  (incf *pointer*)))
(defun move-pointer-left (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(progn (decf *pointer*)))
(defun increment-byte (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(incf (aref *tape* *pointer*)))
(defun decrement-byte (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(decf (aref *tape* *pointer*)))
(defun start-loop (stream ch)
 (declare (ignore ch))
 (let ((loop-body (read-delimited-list #\] stream t)))
 `(loop :until (zerop (aref *tape* *pointer*))
        :do ,@loop-body)))
(defun print-one-char (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(with-output-to-string (s *output*) (write-char (code-char (aref *tape* *pointer*)) s)))
(defun read-one-char (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(setf (aref *tape* *pointer*) (char-code (read-char *standard-input*))))
(defun flush-output (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(progn *output*))
(defun reset-me (a b)
 (declare (ignore a))
 (declare (ignore b))
 '(progn 
  (setf *output* (make-array '(0) :element-type 'base-char :fill-pointer 0 :adjustable t))
  (adjust-array *tape* '(1))
  (setf (aref *tape* 0) 0)
  (setf *pointer* 0)))
(set-macro-character #\< #'move-pointer-left)
(set-macro-character #\> #'move-pointer-right)
(set-macro-character #\+ #'increment-byte)
(set-macro-character #\[ #'start-loop)
(set-macro-character #\= #'flush-output)
(set-macro-character #\. #'print-one-char)
(set-macro-character #\, #'read-one-char)
(set-macro-character #\! #'reset-me)
(set-macro-character #\- #'decrement-byte)
  • 输入不起作用
  • 我不确定嵌套循环是否可行,因为“[”读取到“]”并且如果你尝试“[/ commands [/ more] / dubious]”我不知道如何/可疑这些方法可以加载
  • 我尝试了“++ [ - &gt; +&gt; +&lt;&lt;]”。据我所知,数组应该有:“0 2 2”但我得到的是“0 2 0”。我总结出一些错误。
  • 我收到很多来自SBCL的警告 - 没有它们会更好:/
  • 是否可以快速输出所有生成的代码(从“move-pointer-right”等函数返回)到文件?
  • 输出保存在一个字符串中,以“=”命令打印。我做到了,因为其他操作在标准输出上打印了许多无用的东西。对我来说这不是一个大问题 - 似乎很容易想象只是打印到文件,而不是这种解决方法。
  • 我很抱歉我的英文可能出错。

编辑:我编辑了代码(再次 - 谢谢你的帮助,Sylwester)。除了输入之外的一切似乎都有效。

  • 关于输入:我使用了read-char,但它不能按我想要的方式工作。例如,D输入“D”。我想重做它,以便在每个,停止评估并等待用户输入。

  • 问题:是否有progn的替代方法不会返回值(我只想评估但不返回)?例如,(what-i-look-for (setf a 1) 1 2)a设置为1,但不会返回2.

1 个答案:

答案 0 :(得分:2)

如果不太了解你认为它应该如何运作,你需要将tapepointeroutput定义为全局变量,最好使用*earmuffs*以便你可以看出它们是全局的。

(defparameter *tape* (make-array '(1) :adjustable t))

然后我注意到>使用默认元素*tape*扩展了nil。因此,对于每个>,你应该将它设置为0,如果它不是真的(除了nil之外每个值都是真的)它似乎也认为pointer始终位于胶带。在执行>>>++++<<<时,其中包含4的元素早已消失。

loop-body是一个全局变量。你应该在这里使用let来破坏包级别变量。您使用loop错误。请参阅Loop for black belts中的示例。例如。

(defun start-loop (stream ch)
 (declare (ignore ch))
 (let ((loop-body (read-delimited-list #\] stream t)))
 `(loop :until (zerop (aref *tape* *pointer*)) 
        :do ,@loop-body)))

注意那里的declare告诉Common Lisp忽略ch没有被使用。嵌套是自read-deliited-list调用新start-loop [后自动完成的。

print-one-char不会根据ascii值添加char,而是将其添加为数字。通常,在BF中立即打印也是很常见的,因此print-char可能会更好。如果要继续将其保留在内存中,直到按=,就可以打印到字符串输入流。

read读取lisp数据。因此,您需要提供#\a而不是a。请改用read-char

我想你有足够的时间来解决这个问题。使用宏和读取器宏看起来很酷,但是很难调试和扩展,因为在添加读取器宏之后,你会遇到包含这些字符的代码的问题。为除[之外的每个操作创建一个函数将简化测试,因为您可以测试它,宏只会扩展为调用它。

(defun move-pointer-left ()
  (assert (> *pointer* 0) (*pointer*) "Tape pointer out of bounds: ~a" *pointer*) 
  (decf *pointer*))

(set-macro-character #\< (constantly '(move-pointer-left)))