PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/module/language/brainfuck/compile-scheme.scm

#
Scheme | 123 lines | 37 code | 23 blank | 63 comment | 0 complexity | cce1b3d4b6d98fb2cb17d7efec73677e MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, GPL-2.0
  1. ;;; Brainfuck for GNU Guile
  2. ;; Copyright (C) 2009, 2013 Free Software Foundation, Inc.
  3. ;; This library is free software; you can redistribute it and/or
  4. ;; modify it under the terms of the GNU Lesser General Public
  5. ;; License as published by the Free Software Foundation; either
  6. ;; version 3 of the License, or (at your option) any later version.
  7. ;;
  8. ;; This library is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;; Lesser General Public License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU Lesser General Public
  14. ;; License along with this library; if not, write to the Free Software
  15. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ;;; Code:
  17. (define-module (language brainfuck compile-scheme)
  18. #:export (compile-scheme))
  19. ;; Compilation of Brainfuck to Scheme is pretty straight-forward. For all of
  20. ;; brainfuck's instructions, there are basic representations in Scheme we
  21. ;; only have to generate.
  22. ;;
  23. ;; Brainfuck's pointer and data-tape are stored in the variables pointer and
  24. ;; tape, where tape is a vector of integer values initially set to zero. Pointer
  25. ;; starts out at position 0.
  26. ;; Our tape is thus of finite length, with an address range of 0..n for
  27. ;; some defined upper bound n depending on the length of our tape.
  28. ;; Define the length to use for the tape.
  29. (define tape-size 30000)
  30. ;; This compiles a whole brainfuck program. This constructs a Scheme code like:
  31. ;; (let ((pointer 0)
  32. ;; (tape (make-vector tape-size 0)))
  33. ;; (begin
  34. ;; <body>
  35. ;; (write-char #\newline)))
  36. ;;
  37. ;; So first the pointer and tape variables are set up correctly, then the
  38. ;; program's body is executed in this context, and finally we output an
  39. ;; additional newline character in case the program does not output one.
  40. ;;
  41. ;; TODO: Find out and explain the details about env, the three return values and
  42. ;; how to use the options. Implement options to set the tape-size, maybe.
  43. (define (compile-scheme exp env opts)
  44. (values
  45. `(let ((pointer 0)
  46. (tape (make-vector ,tape-size 0)))
  47. ,@(compile-body (cdr exp))
  48. (write-char #\newline))
  49. env
  50. env))
  51. ;; Compile a list of instructions to get a list of Scheme codes. As we always
  52. ;; strip off the car of the instructions-list and cons the result onto the
  53. ;; result-list, it will get out in reversed order first; so we have to (reverse)
  54. ;; it on return.
  55. (define (compile-body instructions)
  56. (let iterate ((cur instructions)
  57. (result '()))
  58. (if (null? cur)
  59. (reverse result)
  60. (let ((compiled (compile-instruction (car cur))))
  61. (iterate (cdr cur) (cons compiled result))))))
  62. ;; Compile a single instruction to Scheme, using the direct representations
  63. ;; all of Brainfuck's instructions have.
  64. (define (compile-instruction ins)
  65. (case (car ins)
  66. ;; Pointer moval >< is done simply by something like:
  67. ;; (set! pointer (+ pointer +-1))
  68. ((<bf-move>)
  69. (let ((dir (cadr ins)))
  70. `(set! pointer (+ pointer ,dir))))
  71. ;; Cell increment +- is done as:
  72. ;; (vector-set! tape pointer (+ (vector-ref tape pointer) +-1))
  73. ((<bf-increment>)
  74. (let ((inc (cadr ins)))
  75. `(vector-set! tape pointer (+ (vector-ref tape pointer) ,inc))))
  76. ;; Output . is done by converting the cell's integer value to a character
  77. ;; first and then printing out this character:
  78. ;; (write-char (integer->char (vector-ref tape pointer)))
  79. ((<bf-print>)
  80. '(write-char (integer->char (vector-ref tape pointer))))
  81. ;; Input , is done similarly, read in a character, get its ASCII code and
  82. ;; store it into the current cell:
  83. ;; (vector-set! tape pointer (char->integer (read-char)))
  84. ((<bf-read>)
  85. '(vector-set! tape pointer (char->integer (read-char))))
  86. ;; For loops [...] we use a named let construction to execute the body until
  87. ;; the current cell gets zero. The body is compiled via a recursive call
  88. ;; back to (compile-body).
  89. ;; (let iterate ()
  90. ;; (if (not (= (vector-ref! tape pointer) 0))
  91. ;; (begin
  92. ;; <body>
  93. ;; (iterate))))
  94. ((<bf-loop>)
  95. `(let iterate ()
  96. (if (not (= (vector-ref tape pointer) 0))
  97. (begin
  98. ,@(compile-body (cdr ins))
  99. (iterate)))))
  100. (else (error "unknown brainfuck instruction " (car ins)))))