interpreter works and bug found
This commit is contained in:
parent
3935622a53
commit
0562b4b1b5
3 changed files with 165 additions and 58 deletions
20
add.bf
Normal file
20
add.bf
Normal 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"!
|
63
hello.bf
63
hello.bf
|
@ -1,44 +1,21 @@
|
|||
[ This program prints "Hello World!" and a newline to the screen, its
|
||||
length is 106 active command characters. [It is not the shortest.]
|
||||
|
||||
This loop is an "initial comment loop", a simple way of adding a comment
|
||||
to a BF program such that you don't have to worry about any command
|
||||
characters. Any ".", ",", "+", "-", "<" and ">" characters are simply
|
||||
ignored, the "[" and "]" characters just have to be balanced. This
|
||||
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.
|
||||
+++++ +++++ initialize counter (cell #0) to 10
|
||||
[ use loop to set 70/100/30/10
|
||||
> +++++ ++ add 7 to cell #1
|
||||
> +++++ +++++ add 10 to cell #2
|
||||
> +++ add 3 to cell #3
|
||||
> + add 1 to cell #4
|
||||
<<<< - decrement counter (cell #0)
|
||||
]
|
||||
++++++++ Set Cell #0 to 8
|
||||
[
|
||||
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4
|
||||
[ as the cell will be cleared by the loop
|
||||
>++ Add 2 to Cell #2
|
||||
>+++ Add 3 to Cell #3
|
||||
>+++ Add 3 to Cell #4
|
||||
>+ Add 1 to Cell #5
|
||||
<<<<- Decrement the loop counter in Cell #1
|
||||
] Loop till Cell #1 is zero; number of iterations is 4
|
||||
>+ Add 1 to Cell #2
|
||||
>+ Add 1 to Cell #3
|
||||
>- Subtract 1 from Cell #4
|
||||
>>+ 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
|
||||
|
||||
> ++ . print 'H'
|
||||
> + . print 'e'
|
||||
+++++ ++ . print 'l'
|
||||
. print 'l'
|
||||
+++ . print 'o'
|
||||
> ++ . print ' '
|
||||
<< +++++ +++++ +++++ . print 'W'
|
||||
> . print 'o'
|
||||
+++ . print 'r'
|
||||
----- - . print 'l'
|
||||
----- --- . print 'd'
|
||||
> + . print '!'
|
||||
> . print '\n'
|
||||
|
|
140
main.rkt
140
main.rkt
|
@ -24,8 +24,8 @@
|
|||
; - "-" (sub1)
|
||||
; - "." (out)
|
||||
; - ","
|
||||
; - "["
|
||||
; - "]"
|
||||
; - "[" (loop-start)
|
||||
; - "]" (loop-end)
|
||||
; Interpretation: the brainf*ck program.
|
||||
|
||||
; A InstructionPointer (IP) is a NonNegInt
|
||||
|
@ -51,7 +51,8 @@
|
|||
(world-ip w)))
|
||||
|
||||
; 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)
|
||||
(local [; valid-char: 1String -> Boolean
|
||||
; Given a valid-char, returns #t if the character is a valid bf
|
||||
|
@ -113,6 +114,14 @@
|
|||
(world-program 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
|
||||
; Given a world, returns a new world with the - instruction executed
|
||||
(define (exec-sub1 w)
|
||||
|
@ -123,6 +132,14 @@
|
|||
(world-program 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
|
||||
; Given a world, returns a new world with the < instruction executed
|
||||
(define (exec-tape-left w)
|
||||
|
@ -135,13 +152,18 @@
|
|||
(world-program 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
|
||||
; Given a world, returns a new world with the > instruction executed
|
||||
(define (exec-tape-right w)
|
||||
(local [(define end-of-tape (= (world-dp w) (sub1 (world-tape-len w))))]
|
||||
(make-world
|
||||
(if end-of-tape
|
||||
(cons (world-tape w) 0)
|
||||
(append (world-tape w) (list 0))
|
||||
(world-tape w))
|
||||
(add1 (world-dp w))
|
||||
(if end-of-tape
|
||||
|
@ -151,6 +173,12 @@
|
|||
(world-program 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
|
||||
; Given a world, returns a new world with the . instruction executed
|
||||
(define (exec-out w)
|
||||
|
@ -165,10 +193,39 @@
|
|||
(world-program w)
|
||||
(add1 (world-ip w))))
|
||||
|
||||
; find-first: 1String String Nat -> Nat
|
||||
(define (find-first char string start)
|
||||
(cond [(string=? (substring string start (add1 start)) char) start]
|
||||
[else (find-first char string (add1 start))]))
|
||||
; Tests for exec-out
|
||||
(check-expect (exec-out (make-world (list 50) 0 1 "" ".[->+<]" 0))
|
||||
(make-world (list 50) 0 1 "2" ".[->+<]" 1))
|
||||
(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
|
||||
; Given a world, returns a new world with the [ instruction executed
|
||||
|
@ -181,22 +238,75 @@
|
|||
(world-output w)
|
||||
(world-program w)
|
||||
(add1 (if jump
|
||||
(find-first "]" (world-program w) (world-ip w))
|
||||
(find-first "]" (world-program w) (world-ip w) add1)
|
||||
(world-ip w))))))
|
||||
|
||||
; Tests for exec-out
|
||||
(check-expect (exec-out (make-world (list 50) 0 1 "" ".[->+<]" 0))
|
||||
(make-world (list 50) 0 1 "2" ".[->+<]" 1))
|
||||
(check-expect (exec-out (make-world (list 65) 0 1 "" ".[->+<]" 0))
|
||||
(make-world (list 65) 0 1 "A" ".[->+<]" 1))
|
||||
; Tests for exec-loop-start
|
||||
(check-expect (exec-loop-start
|
||||
(make-world '(0) 0 1 "" "[++--]++--+-[]" 0))
|
||||
(make-world '(0) 0 1 "" "[++--]++--+-[]" 6))
|
||||
(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
|
||||
; Given a Filename, return the last world state of execution of the
|
||||
; corresponding bf program in the file
|
||||
(define (main file)
|
||||
(program->world (string->program (read-file file))))
|
||||
(execute (program->world (string->program (read-file file)))))
|
||||
|
||||
; TODO:
|
||||
; fix matching bracket bug
|
||||
; - big-bang:
|
||||
; - - - - - - inital state (make-world (list 0) 0 "" <program> 0)
|
||||
; - - - - - - on-tick fetch, decode, execute
|
||||
|
|
Loading…
Reference in a new issue