commit 7e62e4719850f4b5597f23f965e7d33c1094420a Spenser Truex <struex0@gmail.com> 2016-02-15 19:19:19 -0800 Reader macro, shorter README
README.md | 35 +++++++++-------------------------- interpreter.lisp | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 30 deletions(-)
diff --git a/README.md b/README.md index 83dc419..e4c5f69 100644 --- a/README.md +++ b/README.md @@ -20,43 +20,27 @@ Any other character is considered a "comment" meaning it does nothing. These can be combined into a string such as the following "Hello World!" program: ``` -> (interpret "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.") +> #f++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. "Hello World! " ``` #How to use: Store this project's code in the ASDF load directory; [ASDF](https://common-lisp.net/project/asdf/) must be installed. Then it may be loaded like this: ``` -> (asdf:load-system "brain") +> (asdf:load-system :brain) ``` -The external functions are easier to use when they have been *used*, so execute this command. +Now there is the choice between `brain:fuck` and the `#F` notation. The #F notation is more concise but does not allow any whitespace in the brainfuck code. Below they are both shown ``` -> (use-package :brain) -or alternatively the brainfuck functions may be called using the brain:[function] notation, without being used: -> (brain:fuck "[Put your code in the string]") -``` -Your brainfuck interpreter is now loaded! - -You can run a string of brainfuck code with: -``` -> (interpret "[code]") -``` -For example: -``` -> (interpret ".+[.+] Print out all the ascii characters") +> (brain:fuck ".+[.+] Print out all the ascii characters from 0 to 255") "� !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ" -``` -Take note that the `interpret` command returns a string, which allows the brainfuck to be used within other lisp code. -Finally, there is a slightly more profane alias for the `interpret` function: -``` -> (fuck "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.F+++++++++++++++.U------------------.C++++++++.K") -"FUCK" -;; Also: -> (brain:fuck "[This is a loop comment.]") +>#f.+[.+] ;Note that this comment is not part of the brainfuck because of the space between it and the brainfuck code. +"� + + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ" ``` Debugging brainfuck code can be done using all the normal Common Lisp functions: `step`, `trace`, `time`, etc., but require the user to understand how the internal code works. For this reason there is no tutorial on how to properly debug your brainfuck code, just a couple examples. The user is encouraged to read the Common Lisp source code to get all the features of the Lisp debugging environment in their brainfuck. @@ -89,8 +73,7 @@ Suppose you want to find information about the + and - operations: ``` #TODO: - Test on other implementations and operating systems (CCL, ECL, ABCL, CLISP, AllegroCL, LispWorks), to verify that it works. -- Make debugging more user friendly. -- `#F` reader macro (Ex: `#F.+[.+]` instead of `(fuck ".+[.+]")` +- Make debugging more user friendly. Better documentation and some external functions to be called with `brain:function` or `brain:*variable*` This software is licensed under the MIT free software license. ==== diff --git a/interpreter.lisp b/interpreter.lisp index 6dc0af7..cda089a 100644 --- a/interpreter.lisp +++ b/interpreter.lisp @@ -14,6 +14,8 @@ ;;;; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THIS BRAINFUCK SOFTWARE OR THE USE OR OTHER DEALINGS IN THIS ;;;; BRAINFUCK SOFTWARE. (in-package :brain) +(defconstant +close-bracket+ #\]) +(defconstant +open-bracket+ #\[) (defparameter *tape-size-default* 30000 "The size of the tape, in bytes, used to store each byte") @@ -49,6 +51,33 @@ (setf *pointer* (pointer-default)) (setf *output* "")) +(defun char-list->string-aux (char-list string position) + (if (null (first char-list)) + string + (char-list->string-aux (rest char-list) + (progn (setf (char string position) + (first char-list)) + string) + (1+ position)))) +(defun char-list->string (char-list) + (char-list->string-aux char-list (make-string (length char-list)) 0)) + +(defun shorthand-fuck-aux (stream list) + (let ((char (read-char stream nil nil))) + ;; TODO: Replace the #\Space, #\Newline, #\Tab with proper separations + (if (or (char-equal char #\Space) + (char-equal char #\Newline) + (char-equal char #\Tab) + (null char)) + (char-list->string (nreverse list)) + (shorthand-fuck-aux stream (push char list))))) + +(defun shorthand-fuck (stream char subchar) + "This is a 'Reader Macro' that provides the #F notation" + (declare (ignore char subchar)) + (list 'fuck (shorthand-fuck-aux stream nil))) +(set-dispatch-macro-character #\# #\F #'shorthand-fuck) + (defun wrap-pointer () (cond ((= *pointer* *tape-size-default*) (setf *pointer* 0)) ((= *pointer* -1) (setf *pointer* (1- *tape-size-default*))))) @@ -88,7 +117,7 @@ (defun goto-aux (current-position depth) (let ((this (elt *brainfuck* current-position))) - (if (char= #\[ this) + (if (char= +open-bracket+ this) (if (= 1 depth) current-position (if (> depth 1) @@ -96,7 +125,7 @@ (1- depth)) (goto-aux (1- current-position) 0))) - (if (char= #\] this) + (if (char= +close-bracket+ this) (goto-aux (1- current-position) (1+ depth)) (goto-aux (1- current-position) @@ -139,9 +168,9 @@ (defun skip-loop-aux (position depth) (let ((this (char *brainfuck* position))) - (if (char= #\[ this) + (if (char= +open-bracket+ this) (skip-loop-aux (1+ position) (1+ depth)) - (if (char= #\] this) + (if (char= +close-bracket+ this) (if (= 1 depth) (1+ position) (skip-loop-aux (1+ position) (1- depth)))