#!/usr/bin/newlisp
;; @module brainfuck.lsp
;; @description Brainf*ck Interpreter
;; @version 1.0 - first commit
;; @version 1.1 - speedup by using array. doc changes.
;; @author KOBAYASHI Shigeru <shigeru.kb[at]gmail.com>, 2009-2011
;; @license MIT licence
;; @location https://gist.github.com/242690
;;
;; This file is @link http://en.wikipedia.org/wiki/Brainfuck Brainfuck interpreter
;; written in newLISP. Works newlisp v.10.2.8 (or lator).
;;
;; <h3>command-line options</h3>
;; see details `newlisp brainfuck.lsp -help'
;;
;; @example
;; eval FILENAME with memsize=30000
;; $ newlisp brainfuck.lsp -memsize 30000 -eval-file FILENAME
;;
;; eval FILENAME from standard-input
;; $ cat FILENAME | newlisp brainfuck.lsp -
;;
;; eval from URL-FILE
;; $ newlisp brainfuck.lsp -eval-file http://localhost/bf-source.b
;;
;; convert FILENAME to C program
;; $ newlisp brainfuck.lsp -bfc FILENAME > foo.c
;;
;; print "Hello World!"
;; $ newlisp brainfuck.lsp -hello
;;
;; read and print loop
;; $ newlisp brainfuck.lsp -eval ",[.,]"
;;; Code:
(context 'Brainfuck)
(setf stdin 0 stdout 1 stderr 2)
(define (make-vector size)
(array size '(0))
;(dup 0 size)
)
(setf memsize 512)
(setf memory (make-vector memsize))
(define (trim-comment src)
(replace "[^[]+,.<>[-]]" src "" 0))
(define (Brainfuck:eval src)
(setf src (trim-comment src))
(let ((ptr 0) (i 0)
(srclen (length src)))
(setf memory (make-vector memsize))
(catch
(while (< i srclen)
;; (begin (! "tput clear") (println (i src)) (sleep 25))
(case (src i)
(">" (++ ptr))
("<" (-- ptr))
("+" (++ (memory ptr)))
("-" (-- (memory ptr)))
("." (write-char stdout (memory ptr)))
("," (setf (memory ptr) (or (read-char stdin)
(throw 'eof))))
("[" (when (= (memory ptr) 0)
(let ((level 1))
(while (!= level 0)
(++ i)
(case (src i)
("[" (++ level))
("]" (-- level)))))))
("]" (when (!= (memory ptr) 0)
(let ((level 1))
(while (!= level 0)
(-- i)
(case (src i)
("[" (-- level))
("]" (++ level))))))))
(++ i))))
true)
(define (bfc src-text)
(let ((buffer "")
(-> (lambda ()
(write-line buffer (apply string (args))))))
;; (-> "/* " src-text "*/")
(-> "#include <stdio.h>")
(-> "int main() {")
(-> " char mem[" memsize "]={0};")
(-> " char *p=mem;")
(dostring (c src-text)
(case (char c)
(">" (-> "++p;"))
("<" (-> "--p;"))
("+" (-> "++*p;"))
("-" (-> "--*p;"))
("." (-> "putchar(*p);"))
("," (-> "*p = getchar();"))
("[" (-> "while (*p) {"))
("]" (-> "}"))))
(-> " return 0;")
(-> "}")
buffer))
;(define (bfc-optimize src-text) )
(define (eval-file filename)
(Brainfuck:eval (read-file! filename)))
(define (bfc-file filename)
(bfc (read-file! filename)))
(define (eval-stream (fd stdin))
(Brainfuck:eval (echo fd "")))
(define (hello)
(Brainfuck:eval [text]
// print "Hello World!"
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+
++++++..+++.>++.<<+++++++++++++++.>.+++.------.---
-----.>+.>.
[/text]))
;;; Utility functions
(define (read-file! filename)
(or (read-file filename)
;; :if-does-not-exist
(throw-error (cons filename (sys-error)))))
(define (echo in (out stdout))
(while (read-line in)
(write-line out))
(cond ((string? out) out)
("else" true)))
;;; Call interactively
(signal 2 exit) ; SIGINT (Ctrl-C)
;;;###Usage
(setf help-text [text]
Brainf*ck interpreter for newLISP
Useage: newlisp brainfuck.lsp [option]... [file]...
Options:
-memsize MEMSIZE set internal memory size MEMSIZE
-eval TEXT eval TEXT directly
-eval-file FILENAME eval from FILENAME
- eval from standard intput
-bfc FILENAME convert FILENAME to C program
-cc FILENAME same as `-bfc' and execute it [for debug]
-help display this message
[/text])
(dolist (arg $main-args)
(case arg
("-memsize" (setf memsize (or (int (main-args (+ $idx 1))) memsize)))
("-eval" (Brainfuck:eval (main-args (+ $idx 1)))
(exit))
("-eval-file" (eval-file (main-args (+ $idx 1)))
(exit))
("-bfc" (print (bfc-file (main-args (+ $idx 1))))
(exit))
("-" (eval-stream stdin)
(exit))
("-hello" (hello)
(exit))
("-cc" (let (cfile "bfc.c")
(and (write-file cfile (bfc-file (main-args (+ $idx 1))))
(! (println
(case ostype
("Win32" ; require mingw-gcc
(setq cfile (real-path cfile))
(format {gcc "%s" && a.exe && del a.exe "%s"} cfile cfile))
(true
(format {gcc "%s" && ./a.out && rm a.out "%s"} cfile cfile))))))
(exit)))
("-help" (print help-text)
(exit))
))
(context MAIN)
;;; EOF
syntax highlighting with newLISP and newLISPdoc