Hi Brian,
Looking at what your code does and what I suggested I don't see a difference.
Your command/2 will subtract 1 from dataprt and if dataptr < 0 then you will reset dataptr to 0 and pop data.
My solution took the liberty of saying "if dataptr=0 (before you subtract) we have to pop data otherwise we subtract one from dataprt and that is it".
That should do the same as your code, because if dataptr=0 when you run your command/2 function you will end up with dataptr=-1 before calling do_trim, which will reset it to 0 and pop data.
Reading the Brianfuck page on Wikipedia seems to suggest that the < command should only decrement the dataptr, so I am not sure what the pop you are doing with tl is good for. But then again, Brainfuck is not supposed to be easy to understand...
I actually can't believe that I am going to write the following... ;-)
I would consider using two lists to hold the array of cells. Say, left and right fields in your state.
Initially we would have left=[] and right= "a bunch of zeros".
Then we get something like this
def command("<", state = %{left: []}) do
state # we silently do nothing when moving too far
end
def command("<", state = %{left: [h|t]} do
%{state | left: t, right: [h | state.right]}
end
# we always leave one element in right as that is the end of our world
def command(">", state = %{right: [_]} do
state
end
def command(">", state = %{right: [h|t]}) do
%{state | left: [h | state.left], right: t}
end
def command("+", state = %{right: [h|t]}) do
%{state | right: [ h+1 | t ]} # should probably do some overflow handling if we want to keep having a cell as a byte
end
...
def command("[", state = %{right: [0|t]}) do
state
# this is where the fun starts, now we need to enter a mode where we loop the code
# until we have balanced out [ and ].
...
end
And already here I can see that using command with just two arguments will probably be hurtful in the long run.
Perhaps the function should be called interpret and take a list of chars as the first argument and simply peel off the first element.
When we reach a loop we have to start accumulating the commands in case the loop has to run more than once.
But if we call a loop function that executes commands and return the collected loop body as well as an updated state, then we simply have to check the value at the data pointer when the loop has been collected. If it is zero we throw away the collected program, if not we re-insert the collected program in front of the rest of the program and call interpret again.
Now I am almost tempted to go write the full interpreter, but luckily for me it is late at my end right now, so I'll have to see if I am up for it tomorrow!!
Cheers,
Torben