interpreter works and bug found

This commit is contained in:
Tommaso Rodolfo Masera 2018-11-30 13:18:59 +01:00
parent 3935622a53
commit 0562b4b1b5
3 changed files with 165 additions and 58 deletions

20
add.bf Normal file
View file

@ -0,0 +1,20 @@
++ Cell c0 = 2
> +++++ Cell c1 = 5
[ Start your loops with your cell pointer on the loop counter (c1 in our case)
< + Add 1 to c0
> - Subtract 1 from c1
] End your loops with the cell pointer on the loop counter
At this point our program has added 5 to 2 leaving 7 in c0 and 0 in c1
but we cannot output this value to the terminal since it is not ASCII encoded!
To display the ASCII character "7" we must add 48 to the value 7
48 = 6 * 8 so let's use another loop to help us!
++++ ++++ c1 = 8 and this will be our loop counter again
[
< +++ +++ Add 6 to c0
> - Subtract 1 from c1
]
< . Print out c0 which has the value 55 which translates to "7"!

View file

@ -1,44 +1,21 @@
[ This program prints "Hello World!" and a newline to the screen, its +++++ +++++ initialize counter (cell #0) to 10
length is 106 active command characters. [It is not the shortest.] [ use loop to set 70/100/30/10
> +++++ ++ add 7 to cell #1
This loop is an "initial comment loop", a simple way of adding a comment > +++++ +++++ add 10 to cell #2
to a BF program such that you don't have to worry about any command > +++ add 3 to cell #3
characters. Any ".", ",", "+", "-", "<" and ">" characters are simply > + add 1 to cell #4
ignored, the "[" and "]" characters just have to be balanced. This <<<< - decrement counter (cell #0)
loop and the commands it contains are ignored because the current cell
defaults to a value of 0; the 0 value causes this loop to be skipped.
] ]
++++++++ Set Cell #0 to 8 > ++ . print 'H'
[ > + . print 'e'
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4 +++++ ++ . print 'l'
[ as the cell will be cleared by the loop . print 'l'
>++ Add 2 to Cell #2 +++ . print 'o'
>+++ Add 3 to Cell #3 > ++ . print ' '
>+++ Add 3 to Cell #4 << +++++ +++++ +++++ . print 'W'
>+ Add 1 to Cell #5 > . print 'o'
<<<<- Decrement the loop counter in Cell #1 +++ . print 'r'
] Loop till Cell #1 is zero; number of iterations is 4 ----- - . print 'l'
>+ Add 1 to Cell #2 ----- --- . print 'd'
>+ Add 1 to Cell #3 > + . print '!'
>- Subtract 1 from Cell #4 > . print '\n'
>>+ Add 1 to Cell #6
[<] Move back to the first zero cell you find; this will
be Cell #1 which was cleared by the previous loop
<- Decrement the loop Counter in Cell #0
] Loop till Cell #0 is zero; number of iterations is 8
The result of this is:
Cell No : 0 1 2 3 4 5 6
Contents: 0 0 72 104 88 32 8
Pointer : ^
>>. Cell #2 has value 72 which is 'H'
>---. Subtract 3 from Cell #3 to get 101 which is 'e'
+++++++..+++. Likewise for 'llo' from Cell #3
>>. Cell #5 is 32 for the space
<-. Subtract 1 from Cell #4 for 87 to give a 'W'
<. Cell #3 was set to 'o' from the end of 'Hello'
+++.------.--------. Cell #3 for 'rl' and 'd'
>>+. Add 1 to Cell #5 gives us an exclamation point
>++. And finally a newline from Cell #6

140
main.rkt
View file

@ -24,8 +24,8 @@
; - "-" (sub1) ; - "-" (sub1)
; - "." (out) ; - "." (out)
; - "," ; - ","
; - "[" ; - "[" (loop-start)
; - "]" ; - "]" (loop-end)
; Interpretation: the brainf*ck program. ; Interpretation: the brainf*ck program.
; A InstructionPointer (IP) is a NonNegInt ; A InstructionPointer (IP) is a NonNegInt
@ -51,7 +51,8 @@
(world-ip w))) (world-ip w)))
; string->program: String -> Program ; string->program: String -> Program
; Given a string, returns a b(world-dp-len w)f program without any invalid character ; Given a string, returns a b(world-dp-len w)f program without
; any invalid character
(define (string->program s) (define (string->program s)
(local [; valid-char: 1String -> Boolean (local [; valid-char: 1String -> Boolean
; Given a valid-char, returns #t if the character is a valid bf ; Given a valid-char, returns #t if the character is a valid bf
@ -113,6 +114,14 @@
(world-program w) (world-program w)
(add1 (world-ip w)))) (add1 (world-ip w))))
; Tests for exec-add1
(check-expect (exec-add1 (make-world
(list 1 2 3 4 5 6 7) 3 7 "" "+" 0))
(make-world (list 1 2 3 5 5 6 7) 3 7 "" "+" 1))
(check-expect (exec-add1 (make-world
(list 255 1 2 3) 0 4 "" "+" 0))
(make-world (list 0 1 2 3) 0 4 "" "+" 1))
; exec-sub1: World -> World ; exec-sub1: World -> World
; Given a world, returns a new world with the - instruction executed ; Given a world, returns a new world with the - instruction executed
(define (exec-sub1 w) (define (exec-sub1 w)
@ -123,6 +132,14 @@
(world-program w) (world-program w)
(add1 (world-ip w)))) (add1 (world-ip w))))
; Tests for exec-sub1
(check-expect (exec-sub1 (make-world
(list 1 2 3 4 5 6 7) 3 7 "" "-" 0))
(make-world (list 1 2 3 3 5 6 7) 3 7 "" "-" 1))
(check-expect (exec-sub1 (make-world
(list 0 1 2 3) 0 4 "" "-" 0))
(make-world (list 255 1 2 3) 0 4 "" "-" 1))
; exec-tape-left: World -> World ; exec-tape-left: World -> World
; Given a world, returns a new world with the < instruction executed ; Given a world, returns a new world with the < instruction executed
(define (exec-tape-left w) (define (exec-tape-left w)
@ -135,13 +152,18 @@
(world-program w) (world-program w)
(add1 (world-ip w))))) (add1 (world-ip w)))))
; Tests for exec-tape-left
(check-error (exec-tape-left (make-world (list 1 2 3) 0 3 "" "<" 0)))
(check-expect (exec-tape-left (make-world (list 1 2 3) 2 3 "" "<" 0))
(make-world (list 1 2 3) 1 3 "" "<" 1))
; exec-tape-right: World -> World ; exec-tape-right: World -> World
; Given a world, returns a new world with the > instruction executed ; Given a world, returns a new world with the > instruction executed
(define (exec-tape-right w) (define (exec-tape-right w)
(local [(define end-of-tape (= (world-dp w) (sub1 (world-tape-len w))))] (local [(define end-of-tape (= (world-dp w) (sub1 (world-tape-len w))))]
(make-world (make-world
(if end-of-tape (if end-of-tape
(cons (world-tape w) 0) (append (world-tape w) (list 0))
(world-tape w)) (world-tape w))
(add1 (world-dp w)) (add1 (world-dp w))
(if end-of-tape (if end-of-tape
@ -151,6 +173,12 @@
(world-program w) (world-program w)
(add1 (world-ip w))))) (add1 (world-ip w)))))
; Tests for exec-tape-right
(check-expect (exec-tape-right (make-world (list 1 2 3) 0 3 "" ">" 0))
(make-world (list 1 2 3) 1 3 "" ">" 1))
(check-expect (exec-tape-right (make-world (list 0 1 2) 2 3 "" ">" 0))
(make-world (list 0 1 2 0) 3 4 "" ">" 1))
; exec-out: World -> World ; exec-out: World -> World
; Given a world, returns a new world with the . instruction executed ; Given a world, returns a new world with the . instruction executed
(define (exec-out w) (define (exec-out w)
@ -165,10 +193,39 @@
(world-program w) (world-program w)
(add1 (world-ip w)))) (add1 (world-ip w))))
; find-first: 1String String Nat -> Nat ; Tests for exec-out
(define (find-first char string start) (check-expect (exec-out (make-world (list 50) 0 1 "" ".[->+<]" 0))
(cond [(string=? (substring string start (add1 start)) char) start] (make-world (list 50) 0 1 "2" ".[->+<]" 1))
[else (find-first char string (add1 start))])) (check-expect (exec-out (make-world (list 65) 0 1 "" ".[->+<]" 0))
(make-world (list 65) 0 1 "A" ".[->+<]" 1))
; char-at: String Nat -> 1String
; Given a string and an index, returns the 1String at the position pointed by
; index
(define (char-at s i)
(substring s i (add1 i)))
; Tests for char-at
(check-expect (char-at "malusa" 2) "l")
(check-error (char-at "another string" 300))
; find-first: 1String String Nat (Nat -> Nat) -> Nat
; Given a 1String `char`, a String `string` and a start index, returns
; the position of the first occurrence of `char` in `string` starting from the
; index walking the string changing the index using the `walk` function.
(define (find-first char string start walk)
(cond [(string=? (substring string start (add1 start))
char) start]
[else (find-first char string (walk start) walk)]))
; Tests for find-first
(check-expect (find-first "]" "[++++++---->><-]++++]+--" 0 add1)
15)
(check-expect (find-first "]" "[+++++][+---->><-]++++]+--" 7 add1)
17)
(check-expect (find-first "[" "[+++++][+---->><-]++++]+--" 17 sub1)
7)
; exec-loop-start: World -> World ; exec-loop-start: World -> World
; Given a world, returns a new world with the [ instruction executed ; Given a world, returns a new world with the [ instruction executed
@ -181,22 +238,75 @@
(world-output w) (world-output w)
(world-program w) (world-program w)
(add1 (if jump (add1 (if jump
(find-first "]" (world-program w) (world-ip w)) (find-first "]" (world-program w) (world-ip w) add1)
(world-ip w)))))) (world-ip w))))))
; Tests for exec-out ; Tests for exec-loop-start
(check-expect (exec-out (make-world (list 50) 0 1 "" ".[->+<]" 0)) (check-expect (exec-loop-start
(make-world (list 50) 0 1 "2" ".[->+<]" 1)) (make-world '(0) 0 1 "" "[++--]++--+-[]" 0))
(check-expect (exec-out (make-world (list 65) 0 1 "" ".[->+<]" 0)) (make-world '(0) 0 1 "" "[++--]++--+-[]" 6))
(make-world (list 65) 0 1 "A" ".[->+<]" 1)) (check-expect (exec-loop-start
(make-world '(1) 0 1 "" "[++--]++--+-[]" 0))
(make-world '(1) 0 1 "" "[++--]++--+-[]" 1))
; exec-loop-end: World -> World
; Given a world, returns a new world with the ] instruction executed
(define (exec-loop-end w)
(local [(define jump (not (zero? (list-ref (world-tape w) (world-dp w)))))]
(make-world
(world-tape w)
(world-dp w)
(world-tape-len w)
(world-output w)
(world-program w)
(add1 (if jump
(find-first "[" (world-program w) (world-ip w) sub1)
(world-ip w))))))
; Tests for exec-loop-end
(check-expect (exec-loop-end
(make-world '(0) 0 1 "" "[++--]++--+-[]" 5))
(make-world '(0) 0 1 "" "[++--]++--+-[]" 6))
(check-expect (exec-loop-end
(make-world '(1) 0 1 "" "[++--]++--+-[]" 5))
(make-world '(1) 0 1 "" "[++--]++--+-[]" 1))
; execute: World -> World
; Given an initial World state, returns the final World state executing the
; program.
(define (execute w)
(local [(define program-len (string-length (world-program w)))]
(cond [(>= (world-ip w) program-len) w]
[else
(local [(define inst
(char-at (world-program w) (world-ip w)))]
(execute (cond [(string=? inst "+") (exec-add1 w)]
[(string=? inst "-") (exec-sub1 w)]
[(string=? inst "<") (exec-tape-left w)]
[(string=? inst ">") (exec-tape-right w)]
[(string=? inst "[") (exec-loop-start w)]
[(string=? inst "]") (exec-loop-end w)]
[(string=? inst ".") (exec-out w)]
[(string=? inst ",") ...])))])))
; Tests for execute
(check-expect (execute (make-world (list 0) 0 3 "" "" 0))
(make-world (list 0) 0 3 "" "" 0))
; assert that 5+2 to ASCII = "7" (WTF)
(check-expect (execute (make-world (list 0) 0 1 ""
"++>+++++[<+>-]++++++++[<++++++>-]<." 0))
(make-world (list 55 0) 0 2 "7"
"++>+++++[<+>-]++++++++[<++++++>-]<." 35))
; main: Filename -> World ; main: Filename -> World
; Given a Filename, return the last world state of execution of the ; Given a Filename, return the last world state of execution of the
; corresponding bf program in the file ; corresponding bf program in the file
(define (main file) (define (main file)
(program->world (string->program (read-file file)))) (execute (program->world (string->program (read-file file)))))
; TODO: ; TODO:
; fix matching bracket bug
; - big-bang: ; - big-bang:
; - - - - - - inital state (make-world (list 0) 0 "" <program> 0) ; - - - - - - inital state (make-world (list 0) 0 "" <program> 0)
; - - - - - - on-tick fetch, decode, execute ; - - - - - - on-tick fetch, decode, execute