I am writing a lexer for Brainfuck with Ocamllex, and to implement its loop, I need to change the state of lexbuf so it can returns to a previous position in the stream.
Background info on Brainfuck (skippable)
in Brainfuck, a loop is accomplished by a pair of square brackets with the following rule:
[
-> proceed and evaluate the next token]
-> if the current cell's value is not 0, return to the matching[
Thus, the following code evaluates to 15:
+++ [ > +++++ < - ] > .
it reads:
- In the first cell, assign 3 (increment 3 times)
- Enter loop, move to the next cell
- Assign 5 (increment 5 times)
- Move back to the first cell, and subtract 1 from its value
- Hit the closing square bracket, now the current cell (first) is equals to 2, thus jumps back to
[
and proceed into the loop again- Keep going until the first cell is equals to 0, then exit the loop
- Move to the second cell and output the value with
.
The value in the second cell would have been incremented to 15 (incremented by 5 for 3 times).
Problem:
Basically, I wrote two functions to take care of pushing and popping the last position of the last [
in the header section of brainfuck.mll
file, namely push_curr_p
and pop_last_p
which pushes and pops the lexbuf's current position to a int list ref
named loopstack
:
{ (* Header *)
let tape = Array.make 100 0
let tape_pos = ref 0
let loopstack = ref []
let push_curr_p (lexbuf: Lexing.lexbuf) =
let p = lexbuf.Lexing.lex_curr_p in
let curr_pos = p.Lexing.pos_cnum in
(* Saving / pushing the position of `[` to loopstack *)
( loopstack := curr_pos :: !loopstack
; lexbuf
)
let pop_last_p (lexbuf: Lx.lexbuf) =
match !loopstack with
| [] -> lexbuf
| hd :: tl ->
(* This is where I attempt to bring lexbuf back *)
( lexbuf.Lexing.lex_curr_p <- { lexbuf.Lexing.lex_curr_p with Lexing.pos_cnum = hd }
; loopstack := tl
; lexbuf
)
}
{ (* Rules *)
rule brainfuck = parse
| '[' { brainfuck (push_curr_p lexbuf) }
| ']' { (* current cell's value must be 0 to exit the loop *)
if tape.(!tape_pos) = 0
then brainfuck lexbuf
(* this needs to bring lexbuf back to the previous `[`
* and proceed with the parsing
*)
else brainfuck (pop_last_p lexbuf)
}
(* ... other rules ... *)
}
The other rules work just fine, but it seems to ignore [
and ]
. The problem is obviously at the loopstack
and how I get and set lex_curr_p
state. Would appreciate any leads.