Compiler construction: Theory and practice