| 20 | | ; --------------- |
|---|
| 21 | | ; Version history |
|---|
| 22 | | ; --------------- |
|---|
| 23 | | ; |
|---|
| 24 | | ; v 0.2 |
|---|
| 25 | | ; - Massive speed improvements by using an improved assign algorithm and using hash-tables for bracket lookup. |
|---|
| 26 | | ; |
|---|
| 27 | | |
|---|
| 28 | | ; v 0.1 |
|---|
| 29 | | ; - Initial release |
|---|
| 30 | | |
|---|
| 31 | | ;;;;;;;;;;; VARIABLE INITIALISATION ;;;;;;;;;;; |
|---|
| 32 | | |
|---|
| 33 | | (define (make-hash) (make-hash-table 'equal)) |
|---|
| 34 | | (define (hash-put! table key value) (hash-table-put! table key value)) |
|---|
| 35 | | (define (hash-get table key) (hash-table-get table key (lambda () 'none))) |
|---|
| 36 | | |
|---|
| 37 | | (define memory (make-vector 30000)) |
|---|
| 38 | | (define myptr 0) |
|---|
| 39 | | (define string-ptr 0) |
|---|
| 40 | | (define loop-begin 0) |
|---|
| 41 | | (define mystring "") |
|---|
| 42 | | (define my-open-hash (make-hash)) |
|---|
| 43 | | (define my-close-hash (make-hash)) |
|---|
| 44 | | (define stored-length 0) |
|---|
| 45 | | (define stored-length2 0) |
|---|
| 46 | | (define comma-flag 0) |
|---|
| 47 | | ;The following all have to be procedures because of the dynamic values of memory and myptr. If they were hard-defined (as values and not as procedures) then they would be stuck with the initial value. |
|---|
| 48 | | |
|---|
| 49 | | (define (inc!) (if (= myptr 29999) (set! myptr 0) ;30000 cell wraparound hardcoded |
|---|
| 50 | | (set! myptr (add1 myptr)))) |
|---|
| 51 | | (define (dec!) (if (= myptr 0) (set! myptr 29999) ;30000 cell wraparound again |
|---|
| 52 | | (set! myptr (sub1 myptr)))) |
|---|
| 53 | | |
|---|
| 54 | | (define (plus!) (if (= (vector-ref memory myptr) 255) (vector-set! memory myptr 0) ;8 bit integer wraparound hardcoded |
|---|
| 55 | | (vector-set! memory myptr (add1 (vector-ref memory myptr))))) |
|---|
| 56 | | |
|---|
| 57 | | (define (minus!) (if (= (vector-ref memory myptr) 0) (vector-set! memory myptr 255) ;8 bit wraparound hardcoded again |
|---|
| 58 | | (vector-set! memory myptr (sub1 (vector-ref memory myptr))))) |
|---|
| 59 | | |
|---|
| 60 | | (define (dot!) (display (integer->char (vector-ref memory myptr)))) |
|---|
| 61 | | |
|---|
| 62 | | (define (comma!) (begin (if (= comma-flag 0) (display "Enter data as one character after another without spaces:")) |
|---|
| 63 | | (set! comma-flag 1) |
|---|
| 64 | | (let ((bit (char->integer (read-char)))) |
|---|
| 65 | | (vector-set! memory myptr bit)))) |
|---|
| 66 | | |
|---|
| 67 | | |
|---|
| 68 | | |
|---|
| 69 | | ;Brackets only operate on the string-pointer. |
|---|
| 70 | | |
|---|
| 71 | | ;if the next character in the list is a close-bracket, cons them together. |
|---|
| 72 | | ;else (i.e. the next character is another open-bracket) [1[2[3]]]) |
|---|
| 73 | | ;then you need to find the correct bracket to cons it with, hence find the first unbalanced close-bracket, looking right. |
|---|
| 74 | | ; |
|---|
| 75 | | ;For each bracket, remove it from the list of brackets. Then find the unmatched close bracket. |
|---|
| 76 | | ;So (cdr lst1) and then go through the values until you get -1 in which case this is the unmatched close bracket. |
|---|
| 77 | | ;so cons them together. |
|---|
| 78 | | (define (remove! obj lst) |
|---|
| 79 | | (define (remove-helper! lst obj front-ptr) |
|---|
| 80 | | (cond ((equal? (car lst) obj) (cdr lst)) |
|---|
| 81 | | ((< (length lst) 2) (cond ((null? lst) '()) |
|---|
| 82 | | ((equal? (car lst) obj) '()) |
|---|
| 83 | | (else front-ptr))) |
|---|
| 84 | | ((equal? (second lst) obj) (begin (set-cdr! lst (cddr lst)) |
|---|
| 85 | | front-ptr)) |
|---|
| 86 | | (else (remove-helper! (cdr lst) obj front-ptr)))) |
|---|
| 87 | | (remove-helper! lst obj lst)) |
|---|
| 88 | | |
|---|
| 89 | | ;Binary search takes an ascending ordered list and a key |
|---|
| 90 | | ;It looks in the list for the key and returns #f if it cannot find it, otherwise the index at which it is located. |
|---|
| 91 | | (define (binary-search lst key) |
|---|
| 92 | | (define (helper lst key begin end) |
|---|
| 93 | | (if (or (> begin end) (< end begin)) #f |
|---|
| 94 | | (let* ((index (floor (/ (+ begin end) 2))) |
|---|
| 95 | | (test (list-ref lst index))) ;list-ref might be a bad idea, in the next version I will switch to vectors |
|---|
| 96 | | (cond ((< test key) (helper lst key (+ index 1) end)) |
|---|
| 97 | | ((> test key) (helper lst key begin (- index 1))) |
|---|
| 98 | | (else index))))) |
|---|
| 99 | | (helper lst key 0 (- (length lst) 1))) |
|---|
| 100 | | |
|---|
| 101 | | (define (new-assign first lst1 lst2 acc prev-counter counter-list) |
|---|
| 102 | | (cond ((and (< acc 0) (> prev-counter 0)) (list first prev-counter)) |
|---|
| 103 | | ((binary-search lst1 (car counter-list)) |
|---|
| 104 | | (new-assign first lst1 lst2 (add1 acc) (car counter-list)(cdr counter-list))) |
|---|
| 105 | | ((binary-search lst2 (car counter-list)) |
|---|
| 106 | | (new-assign first lst1 lst2 (sub1 acc) (car counter-list) (cdr counter-list))) |
|---|
| 107 | | (else (error "There is a counter in counter-list that isn't in either lst1 or lst2")))) |
|---|
| 108 | | |
|---|
| 109 | | (define (make-counter-list lst1 lst2) |
|---|
| 110 | | (cond ((null? lst1) lst2) |
|---|
| 111 | | ((null? lst2) lst1) |
|---|
| 112 | | ((<= (car lst1) (car lst2)) (cons (car lst1) (make-counter-list (cdr lst1) lst2))) |
|---|
| 113 | | ((> (car lst1) (car lst2)) (cons (car lst2) (make-counter-list lst1 (cdr lst2)))) |
|---|
| 114 | | (else 'error))) |
|---|
| 115 | | |
|---|
| 116 | | (define (go-assign lst1 lst2 hash1 hash2) |
|---|
| 117 | | (if (null? lst1) (list hash1 hash2) |
|---|
| 118 | | (let* ((counter-list (make-counter-list lst1 lst2)) |
|---|
| 119 | | (myval (new-assign (car lst1) (cdr lst1) lst2 0 (car counter-list) (cdr counter-list)))) |
|---|
| 120 | | (begin |
|---|
| 121 | | ;display the percentage of completion |
|---|
| 122 | | (set! stored-length2 (sub1 stored-length2)) |
|---|
| 123 | | (display (* 100.0 (- 1 (/ stored-length2 stored-length))))(display #\%)(newline) |
|---|
| 124 | | (hash-put! hash1 (car myval) (cadr myval)) |
|---|
| 125 | | (hash-put! hash2 (cadr myval) (car myval)) |
|---|
| 126 | | (go-assign (remove! (car myval) lst1) (remove! (cadr myval) lst2) hash1 hash2))))) |
|---|
| 127 | | |
|---|
| 128 | | (define (get-brackets-list string) |
|---|
| 129 | | (define (get-brackets-helper string counter open-list close-list) |
|---|
| 130 | | (cond ((= counter (string-length string)) (begin (set! stored-length (length open-list)) |
|---|
| 131 | | (set! stored-length2 (+ stored-length 0)) ;copy stored-length, instead of creating a pointer to it |
|---|
| 132 | | (go-assign open-list close-list my-open-hash my-close-hash))) |
|---|
| 133 | | ((char=? (string-ref string counter) #\[) |
|---|
| 134 | | (get-brackets-helper string (add1 counter) (append open-list (list counter)) close-list)) |
|---|
| 135 | | ((char=? (string-ref string counter) #\]) |
|---|
| 136 | | (get-brackets-helper string (add1 counter) open-list (append close-list (list counter)))) |
|---|
| 137 | | (else (get-brackets-helper string (+ counter 1) open-list close-list)))) |
|---|
| 138 | | (get-brackets-helper string 0 '() '())) |
|---|
| 139 | | |
|---|
| 140 | | |
|---|
| 141 | | (define (open-bracket) |
|---|
| 142 | | (if (= (vector-ref memory myptr) 0) |
|---|
| 143 | | (set! string-ptr (hash-get my-open-hash string-ptr)))) |
|---|
| 144 | | |
|---|
| 145 | | (define (close-bracket) (set! string-ptr (hash-get my-close-hash string-ptr))) |
|---|
| 146 | | ;Unconditional jump back to corresponding open-bracket. |
|---|
| 147 | | |
|---|
| 148 | | ;General parsing strategy: |
|---|
| 149 | | ;Pseudo-code: |
|---|
| 150 | | ;Termination condition: string-ptr goes out of bounds. |
|---|
| 151 | | ;Else: Read the letter referenced by string-ptr and compute what it does. |
|---|
| 152 | | |
|---|
| 153 | | ;Comment remover procedure |
|---|
| 154 | | ;Type string->string |
|---|
| 155 | | ;Complexity: t(n)~ O(n), s(n) ~ O(1) |
|---|
| 156 | | ;Inputs a string and if the character is not in the specified character set, ignore it and continue. |
|---|
| 157 | | (define (remove-comments string) |
|---|
| 158 | | (define (remove-comments-helper string acc) |
|---|
| 159 | | (if (not (string=? string "")) |
|---|
| 160 | | (let ((f (substring string 0 1))) |
|---|
| 161 | | (if (or (string=? f "+") (string=? f "-") |
|---|
| 162 | | (string=? f ">") (string=? f "<") |
|---|
| 163 | | (string=? f ".") (string=? f ",") |
|---|
| 164 | | (string=? f "[") (string=? f "]")) |
|---|
| 165 | | (remove-comments-helper (substring string 1 (string-length string)) (string-append acc f)) |
|---|
| 166 | | (remove-comments-helper (substring string 1 (string-length string)) acc))) |
|---|
| 167 | | acc)) |
|---|
| 168 | | (remove-comments-helper string "")) |
|---|
| 169 | | |
|---|
| 170 | | (define (bf string) |
|---|
| 171 | | (cond ((>= string-ptr (string-length string)) (newline)) ;modify this to include an automatic reset |
|---|
| 172 | | (else (read-char) |
|---|
| 173 | | (set! mystring string) ;remove comments from the input string (unfortunately this takes too long so I removed it) |
|---|
| 174 | | (display "Generating loop structure. This could take a while...") |
|---|
| 175 | | (get-brackets-list mystring) |
|---|
| 176 | | (display "Done.") |
|---|
| 177 | | (newline) |
|---|
| 178 | | (compute mystring)))) |
|---|
| 179 | | |
|---|
| 180 | | (define (compute string) |
|---|
| 181 | | (if (>= string-ptr (string-length string)) (newline) |
|---|
| 182 | | (let ((first (string-ref string string-ptr))) |
|---|
| 183 | | (cond ((string=? string "") (error: "Null string")) |
|---|
| 184 | | ((char=? #\> first) (begin (inc!) |
|---|
| 185 | | (set! string-ptr (add1 string-ptr)) |
|---|
| 186 | | (compute string))) |
|---|
| 187 | | ((char=? #\< first) (begin (dec!) |
|---|
| 188 | | (set! string-ptr (add1 string-ptr)) |
|---|
| 189 | | (compute string))) |
|---|
| 190 | | ((char=? #\+ first) (begin (plus!) |
|---|
| 191 | | (set! string-ptr (add1 string-ptr)) |
|---|
| 192 | | (compute string))) |
|---|
| 193 | | ((char=? #\- first) (begin (minus!) |
|---|
| 194 | | (set! string-ptr (add1 string-ptr)) |
|---|
| 195 | | (compute string))) |
|---|
| 196 | | ((char=? #\. first) (begin (dot!) |
|---|
| 197 | | (set! string-ptr (add1 string-ptr)) |
|---|
| 198 | | (compute string))) |
|---|
| 199 | | ((char=? #\, first) (begin (comma!) |
|---|
| 200 | | (set! string-ptr (add1 string-ptr)) |
|---|
| 201 | | (compute string))) |
|---|
| 202 | | ((char=? #\[ first) (begin (open-bracket) |
|---|
| 203 | | (set! string-ptr (add1 string-ptr)) |
|---|
| 204 | | (compute string))) |
|---|
| 205 | | ((char=? #\] first) (begin (close-bracket) |
|---|
| 206 | | (compute string))) |
|---|
| 207 | | (else (begin (set! string-ptr (add1 string-ptr)) |
|---|
| 208 | | (compute string))))))) |
|---|
| 209 | | |
|---|
| 210 | | (display "Welcome to SchemeBF v 0.2.3! A scheme interpreter of the brainfuck programming language.") |
|---|
| 211 | | (newline)(display "Amrik S. Kochhar") |
|---|
| 212 | | (newline)(display "amrik[at]mit[dot]edu 05.14.2006") |
|---|
| 213 | | (newline) |
|---|
| 214 | | (define (start) |
|---|
| 215 | | (set! memory (make-vector 30000)) |
|---|
| 216 | | (set! myptr 0) |
|---|
| 217 | | (set! string-ptr 0) |
|---|
| 218 | | (set! loop-begin 0) |
|---|
| 219 | | (set! mystring "") |
|---|
| 220 | | (set! stored-length 0) |
|---|
| 221 | | (set! stored-length2 0) |
|---|
| 222 | | (set! comma-flag 0) |
|---|
| 223 | | (display "Instructions:")(newline) |
|---|
| 224 | | (display "Enter BF code you wish to compile with double quotes e.g. ")(display #\")(display "+++")(display #\")(display":") |
|---|
| 225 | | (newline) |
|---|
| 226 | | (bf (read))) |
|---|
| 227 | | (start) |
|---|
| 228 | | ; |
|---|
| 229 | | ;Here's a sample program I wrote that you can try: |
|---|
| 230 | | ; |
|---|
| 231 | | ;++++++++[>++++++++<-]>+.-----------[>++<-]>+.+++++.---------.++.>++++++++[>++++<-]>.-------[>+++<-]>.<<<++++.------------.[>+>+<<-]>+++++..>--.<++++++++++.<++[<++++++>-]<--.+++.>>>>>+++++++++[>++++++<-]>.--------.++..+. |
|---|
| | 5 | (define run (instructions position) |
|---|
| | 6 | (newline)) |
|---|