PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/module/language/brainfuck/parse.scm

#
Scheme | 95 lines | 35 code | 14 blank | 46 comment | 0 complexity | cfc919e7401b30f25e51cb96514a4175 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
  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. ; While reading a number of instructions in sequence, all of them are cons'ed
  29. ; onto a list of instructions; thus this list gets out in reverse order.
  30. ; Additionally, for "comment characters" (everything not an instruction) we
  31. ; generate <bf-nop> NOP instructions.
  32. ;
  33. ; This routine reverses a list of instructions and removes all <bf-nop>'s on the
  34. ; way to fix these two issues for a read-in list.
  35. (define (reverse-without-nops lst)
  36. (let iterate ((cur lst)
  37. (result '()))
  38. (if (null? cur)
  39. result
  40. (let ((head (car cur))
  41. (tail (cdr cur)))
  42. (if (eq? (car head) '<bf-nop>)
  43. (iterate tail result)
  44. (iterate tail (cons head result)))))))
  45. ; Read in a set of instructions until a terminating ] character is found (or
  46. ; end of file is reached). This is used both for loop bodies and whole
  47. ; programs, so that a program has to be either terminated by EOF or an
  48. ; additional ], too.
  49. ;
  50. ; For instance, the basic program so just echo one character would be:
  51. ; ,.]
  52. (define (read-brainfuck p)
  53. (let iterate ((parsed '()))
  54. (let ((chr (read-char p)))
  55. (cond
  56. ((eof-object? chr)
  57. (let ((parsed (reverse-without-nops parsed)))
  58. (if (null? parsed)
  59. chr ;; pass on the EOF object
  60. parsed)))
  61. ((eqv? chr #\])
  62. (reverse-without-nops parsed))
  63. (else
  64. (iterate (cons (process-input-char chr p) parsed)))))))
  65. ; This routine processes a single character of input and builds the
  66. ; corresponding instruction. Loop bodies are read by recursively calling
  67. ; back (read-brainfuck).
  68. ;
  69. ; For the poiner movement commands >< and the cell increment/decrement +-
  70. ; commands, we only use one instruction form each and specify the direction of
  71. ; the pointer/value increment using an argument to the instruction form.
  72. (define (process-input-char chr p)
  73. (case chr
  74. ((#\>) '(<bf-move> 1))
  75. ((#\<) '(<bf-move> -1))
  76. ((#\+) '(<bf-increment> 1))
  77. ((#\-) '(<bf-increment> -1))
  78. ((#\.) '(<bf-print>))
  79. ((#\,) '(<bf-read>))
  80. ((#\[) `(<bf-loop> ,@(read-brainfuck p)))
  81. (else '(<bf-nop>))))