now based around result; since each parsing step can emit at most one
error until synchronisation is triggered we can store the errors in the
result.
also state is now stored in a ref variable. the copy-and-update is more
pure, but really messes up the let* expression for the expr/error
results.