Refactor UnaryOp and BinaryOp with codes for operations.

This has various benefits:
* The IR is smaller (store a byte instead of a string + type)
* The dispatch codes are faster because they are switches
* It is just cleaner
Typecheck more accurately the body of MethodDefs and Functions.

When a MethodDef or Function returns NoType, its body can be
a statement (possibly NoType). However, if it returns anything
else, its body must typecheck as Nothing.

For this to work, we had to make sure that the infinite while
loops used to translate tail recursive methods can indeed
typecheck as Nothing.