parse.scm 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. ;;; Brainfuck for GNU Guile.
  2. ;; Copyright (C) 2009 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
  16. ;; 02110-1301 USA
  17. ;;; Code:
  18. (define-module (language brainfuck parse)
  19. #:export (read-brainfuck))
  20. ; Purpose of the parse module is to read in brainfuck in text form and produce
  21. ; the corresponding tree representing the brainfuck code.
  22. ;
  23. ; Each object (representing basically a single instruction) is structured like:
  24. ; (<instruction> [arguments])
  25. ; where <instruction> is a symbolic name representing the type of instruction
  26. ; and the optional arguments represent further data (for instance, the body of
  27. ; a [...] loop as a number of nested instructions).
  28. ;
  29. ; A full brainfuck program is represented by the (<brainfuck> instructions)
  30. ; object.
  31. ; While reading a number of instructions in sequence, all of them are cons'ed
  32. ; onto a list of instructions; thus this list gets out in reverse order.
  33. ; Additionally, for "comment characters" (everything not an instruction) we
  34. ; generate <bf-nop> NOP instructions.
  35. ;
  36. ; This routine reverses a list of instructions and removes all <bf-nop>'s on the
  37. ; way to fix these two issues for a read-in list.
  38. (define (reverse-without-nops lst)
  39. (let iterate ((cur lst)
  40. (result '()))
  41. (if (null? cur)
  42. result
  43. (let ((head (car cur))
  44. (tail (cdr cur)))
  45. (if (eq? (car head) '<bf-nop>)
  46. (iterate tail result)
  47. (iterate tail (cons head result)))))))
  48. ; Read in a set of instructions until a terminating ] character is found (or
  49. ; end of file is reached). This is used both for loop bodies and whole
  50. ; programs, so that a program has to be either terminated by EOF or an
  51. ; additional ], too.
  52. ;
  53. ; For instance, the basic program so just echo one character would be:
  54. ; ,.]
  55. (define (read-brainfuck p)
  56. (let iterate ((parsed '()))
  57. (let ((chr (read-char p)))
  58. (cond
  59. ((eof-object? chr)
  60. (let ((parsed (reverse-without-nops parsed)))
  61. (if (null? parsed)
  62. chr ;; pass on the EOF object
  63. parsed)))
  64. ((eqv? chr #\])
  65. (reverse-without-nops parsed))
  66. (else
  67. (iterate (cons (process-input-char chr p) parsed)))))))
  68. ; This routine processes a single character of input and builds the
  69. ; corresponding instruction. Loop bodies are read by recursively calling
  70. ; back (read-brainfuck).
  71. ;
  72. ; For the poiner movement commands >< and the cell increment/decrement +-
  73. ; commands, we only use one instruction form each and specify the direction of
  74. ; the pointer/value increment using an argument to the instruction form.
  75. (define (process-input-char chr p)
  76. (case chr
  77. ((#\>) '(<bf-move> 1))
  78. ((#\<) '(<bf-move> -1))
  79. ((#\+) '(<bf-increment> 1))
  80. ((#\-) '(<bf-increment> -1))
  81. ((#\.) '(<bf-print>))
  82. ((#\,) '(<bf-read>))
  83. ((#\[) `(<bf-loop> ,@(read-brainfuck p)))
  84. (else '(<bf-nop>))))