1. #!/usr/bin/csi -s
    
  2. ; Install required modules:
    
  3. ; $ chicken-install srfi-69
    
  4. (import (chicken io))
    
  5. (import (chicken format))
    
  6. (import (chicken process-context))
    
  7. (import (srfi 69))
    
  8. (import (srfi 4))
    
  9. 
    
  10. (define TAPE_LEN 30000)
    
  11. 
    
  12. ; return hash tables to find matching braces
    
  13. (define (build_jumps program)
    
  14.   ; first element of stack is top of stack
    
  15.   (define (build_jumps_inner i stack forward backward)
    
  16.     (if (>= i (string-length program))
    
  17.       (if (eq? '() stack)
    
  18. 	(cons forward backward)
    
  19. 	(error "jump stack not empty"))
    
  20.       (let ((c (string-ref program i)))
    
  21. 	(cond ((eq? c #\[)
    
  22. 	       (build_jumps_inner (+ 1 i) (cons i stack) forward backward))
    
  23. 	      ((eq? c #\])
    
  24. 	       (let ((open (car stack))
    
  25. 		     (close i)
    
  26. 		     (stack (cdr stack)))
    
  27. 		 (begin
    
  28. 		   (hash-table-set! forward open close)
    
  29. 		   (hash-table-set! backward close open)
    
  30. 		   (build_jumps_inner (+ 1 i) stack forward backward))))
    
  31. 	      (#t (build_jumps_inner (+ 1 i) stack forward backward))))))
    
  32.   (build_jumps_inner 0 '() (make-hash-table) (make-hash-table)))
    
  33. (define (jump table i)
    
  34.   (if (hash-table-exists? table i)
    
  35.     (+ 1 (hash-table-ref table i))
    
  36.     (error "missing jump.")))
    
  37. 
    
  38. (define (tape-inc tape i)
    
  39.   (if (< i 0)
    
  40.     (error "negative index: " i)
    
  41.     (u8vector-set! tape i (modulo (+ (u8vector-ref tape i) 1) 256))))
    
  42. (define (tape-dec tape i)
    
  43.   (if (< i 0)
    
  44.     (error "negative index: " i)
    
  45.     (u8vector-set! tape i (modulo (- (u8vector-ref tape i) 1) 256))))
    
  46. 
    
  47. 
    
  48. (define (bf program)
    
  49.   (define (bf_state pc ptr tape forward backward)
    
  50.     (if (>= pc (string-length program))
    
  51.       "done"
    
  52.       (let ((c (string-ref program pc))
    
  53. 	    (next (+ 1 pc)))
    
  54. 	(cond ((eq? c #\+) (begin
    
  55. 			     (tape-inc tape ptr)
    
  56. 			     (bf_state next ptr tape forward backward)))
    
  57. 	      ((eq? c #\-) (begin
    
  58. 			     (tape-dec tape ptr)
    
  59. 			     (bf_state next ptr tape forward backward)))
    
  60. 	      ((eq? c #\>) (bf_state next (+ ptr 1) tape forward backward))
    
  61. 	      ((eq? c #\<) (bf_state next (- ptr 1) tape forward backward))
    
  62. 	      ((eq? c #\.) (begin
    
  63. 			     (display (integer->char (u8vector-ref tape ptr)))
    
  64. 			     (bf_state next ptr tape forward backward)))
    
  65. 	      ((eq? c #\[) (bf_state
    
  66. 			     (if (= 0 (u8vector-ref tape ptr))
    
  67. 			       (jump forward pc)
    
  68. 			       next)
    
  69. 			     ptr tape forward backward))
    
  70. 	      ((eq? c #\]) (bf_state
    
  71. 			     (if (= 0 (u8vector-ref tape ptr))
    
  72. 			       next
    
  73. 			       (jump backward pc))
    
  74. 			     ptr tape forward backward))
    
  75. 	      (#t (error "unknown instruction" c))))))
    
  76.   (let ((jumps (build_jumps program)))
    
  77.     (bf_state 0 0 (make-u8vector TAPE_LEN 0)
    
  78. 	      (car jumps)
    
  79. 	      (cdr jumps))))
    
  80. 
    
  81. (define (is-comment? line)
    
  82.   (and (> (string-length line) 0)
    
  83.        (eq? #\; (string-ref line 0))))
    
  84. 
    
  85. ; given a list of lines,
    
  86. ; return a string representing the program.
    
  87. ; lines starting with ; are dropped
    
  88. ; newlines between lines are dropped.
    
  89. (define (clean lines acc)
    
  90.   (if (null? lines)
    
  91.     acc
    
  92.     (if (is-comment? (car lines))
    
  93.       (clean (cdr lines) acc)
    
  94.       (clean (cdr lines)
    
  95. 	     (string-append acc (car lines))))))
    
  96. 
    
  97. (define (main filename)
    
  98.   (let* (
    
  99. 	 (file (open-input-file filename))
    
  100. 	 (program (clean (read-lines file) (string))))
    
  101.     (bf program)))
    
  102. (main (car (command-line-arguments)))