Wolfram Language (Mathematica), 212 170 166 164 159 bytes
A_~J~B_=Fold[Switch[{a,b}=#;#2,"f",{A,b},"F",{a,B},"e",{0,b},"E",{a,0},"p",#+Min[a,B-b]z,"P",#-Min[b,A-a]z,"o",Echo@a;#,_,Echo@b;#]&,z={-1,1};0z,Characters@#]&
Calling J[A,B]
returns an interpreter for the (A,B)-bucket system, which can be applied to a program with J[A,B]["prog"]
. The "o" and "O" commands print using Echo
, and the return value of the program is the final bucket state.
The program is a big Switch
statement folded over the characters of the program, step-wise updating the contents of the buckets. Dispatching the Switch
takes a shortcut by defaulting to "O", as the spec says that there are no characters to be expected other than "fFeEpPoO".
Debugging is done by replacing Fold
with FoldList
, so that the return value of the program becomes the sequence of bucket states traversed during program execution. Un-golfed version:
J[A_,B_][prog_] :=
FoldList[ (* debug version *)
Switch[{a,b}=#; (* initialize a and b to the current bucket contents *)
#2, (* switch as a function of the next character in the program *)
"f", {A,b}, (* f: fill first bucket, leave second bucket unchanged *)
"F", {a,B}, (* F: fill second bucket, leave first bucket unchanged *)
"e", {0,b}, (* e: empty first bucket, leave second bucket unchanged *)
"E", {a,0}, (* E: empty second bucket, leave first bucket unchanged *)
"p", {a,b}+{-1,1}*Min[a,B-b], (* p: pour first bucket into second bucket *)
"P", {a,b}+{1,-1}*Min[b,A-a], (* P: pour second bucket into first bucket *)
"o", Echo@a;{a,b}, (* o: print contents of first bucket *)
"O", Echo@b;{a,b}]&, (* O: print contents of second bucket *)
{0,0}, (* start with both buckets empty *)
Characters[prog]] (* fold over the characters in the program string *)