Compiler Construction: Principles and Practice