123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- ;;; Brainfuck for GNU Guile.
- ;; Copyright (C) 2009, 2013 Free Software Foundation, Inc.
- ;; This library is free software; you can redistribute it and/or
- ;; modify it under the terms of the GNU Lesser General Public
- ;; License as published by the Free Software Foundation; either
- ;; version 3 of the License, or (at your option) any later version.
- ;;
- ;; This library is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;; Lesser General Public License for more details.
- ;;
- ;; You should have received a copy of the GNU Lesser General Public
- ;; License along with this library; if not, write to the Free Software
- ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- ;; 02110-1301 USA
- ;;; Code:
- (define-module (language brainfuck parse)
- #:export (read-brainfuck))
- ; Purpose of the parse module is to read in brainfuck in text form and produce
- ; the corresponding tree representing the brainfuck code.
- ;
- ; Each object (representing basically a single instruction) is structured like:
- ; (<instruction> [arguments])
- ; where <instruction> is a symbolic name representing the type of instruction
- ; and the optional arguments represent further data (for instance, the body of
- ; a [...] loop as a number of nested instructions).
- ; While reading a number of instructions in sequence, all of them are cons'ed
- ; onto a list of instructions; thus this list gets out in reverse order.
- ; Additionally, for "comment characters" (everything not an instruction) we
- ; generate <bf-nop> NOP instructions.
- ;
- ; This routine reverses a list of instructions and removes all <bf-nop>'s on the
- ; way to fix these two issues for a read-in list.
- (define (reverse-without-nops lst)
- (let iterate ((cur lst)
- (result '()))
- (if (null? cur)
- result
- (let ((head (car cur))
- (tail (cdr cur)))
- (if (eq? (car head) '<bf-nop>)
- (iterate tail result)
- (iterate tail (cons head result)))))))
- ; Read in a set of instructions until a terminating ] character is found (or
- ; end of file is reached). This is used both for loop bodies and whole
- ; programs, so that a program has to be either terminated by EOF or an
- ; additional ], too.
- ;
- ; For instance, the basic program so just echo one character would be:
- ; ,.]
- (define (read-brainfuck p)
- (let iterate ((parsed '()))
- (let ((chr (read-char p)))
- (cond
- ((eof-object? chr)
- (let ((parsed (reverse-without-nops parsed)))
- (if (null? parsed)
- chr ;; pass on the EOF object
- parsed)))
- ((eqv? chr #\])
- (reverse-without-nops parsed))
- (else
- (iterate (cons (process-input-char chr p) parsed)))))))
- ; This routine processes a single character of input and builds the
- ; corresponding instruction. Loop bodies are read by recursively calling
- ; back (read-brainfuck).
- ;
- ; For the poiner movement commands >< and the cell increment/decrement +-
- ; commands, we only use one instruction form each and specify the direction of
- ; the pointer/value increment using an argument to the instruction form.
- (define (process-input-char chr p)
- (case chr
- ((#\>) '(<bf-move> 1))
- ((#\<) '(<bf-move> -1))
- ((#\+) '(<bf-increment> 1))
- ((#\-) '(<bf-increment> -1))
- ((#\.) '(<bf-print>))
- ((#\,) '(<bf-read>))
- ((#\[) `(<bf-loop> ,@(read-brainfuck p)))
- (else '(<bf-nop>))))
|