My solution isn't precisely what was asked for: it is the complete parser. At the moment, it is strictly a parser, but the functionality is all ready to be added.
Clear[bfinterpreter, bfeval, movf, movb, add1, sub1, write, read, loop,
stored, loopdepth, rls]
rls = {">" -> movf, "<" -> movb, "+" -> add1,
"-" -> sub1, "." -> write, "," -> read,
"]" :> With[{mode = loopdepth--}, loop[stored[mode]]]
};
bfeval[a : (">" | "<" | "+" | "-" | "." | "," | "]")] :=
With[{val = a /. rls},
If[loopdepth == 0,
val[ptr],
AppendTo[stored[loopdepth], val]; ## &[]]
]
bfeval["["] := (stored[++loopdepth] = {}; ## &[])
bfeval[_] := (## &[])
bfinterpreter[code_String] :=
Block[{loopdepth = 0, stored, ptr,
movf, movb, add1, sub1, write, read},
bfeval /@ StringSplit[code, ""]
];
A user would access this by passing bfinterpreter
as String
of BF commands. The string is split into individual characters via StringSplit[code, ""]
, and then bfeval
is mapped over the resulting list. Scan
would be better for the full implementation, but I used Map
here to show that the parser works. In particular, using this on the OPs supplied BF code we get
(* spaces added for demo purposes *)
bfinterpreter["[[-]>> > . <<<]>>> >>>."]
(*
=> {loop[{ (* [ *)
loop[{sub1}], (* [-] *)
movf, (* > *)
movf, (* > *)
movf, (* > *)
write, (* . *)
movb, (* < *)
movb, (* < *)
movb (* < *)
}
][ptr], (* ] *)
movf[ptr], (* > *)
movf[ptr], (* > *)
movf[ptr], (* > *)
movf[ptr], (* > *)
movf[ptr], (* > *)
movf[ptr], (* > *)
write[ptr] (* . *)
}
*)
As you've probably figured out, all the magic happens in bfeval
. The first form accepts all BF characters {">", "<", "+", "-", ".", ",", "]"}
, but the open loop, "[". It works by applying a list of replacement rules to the supplied character and either storing it, if we're within a loop, or evaluating it immediately. The close loop rule, though, has added functionality in that it needs to both reduce loop counter, loopdepth
, and return the stored commands for the loop, hence the use of RuleDelayed
(:>
).
The second form of bfeval
simply increments the loop counter, and returns ##&[]
to ensure the final list doesn't contain Null
s. And, the final form is the catch all for every other type of character.