;;; 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: ; ( [arguments]) ; where 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 NOP instructions. ; ; This routine reverses a list of instructions and removes all '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) ') (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 ((#\>) '( 1)) ((#\<) '( -1)) ((#\+) '( 1)) ((#\-) '( -1)) ((#\.) '()) ((#\,) '()) ((#\[) `( ,@(read-brainfuck p))) (else '())))