Many operations on data structures can be viewed as homomorphisms, that is,
as structure preserving mappings from one domain into another. For instance,
compilers typically map the abstract syntax of the source language into a
specific machine code language1. Other kinds of
abstract interpretations (e.g., pretty-printing and type-checking) should be
expressed as homomorphisms between source and target domain as well. The
reason for this recommendation can be explained by means of an equation that
holds, if a homomorphic relationship between two structures exists:
compile(assign(lhs, rhs)) =First of all, the above equation depicts a general way of shifting the interpretation from operators down to operands. The strictly ``top-down'' recursive nature of this translation process should be recognizable for users of an object-oriented design. Furthermore, the right hand side of equation 1 has a structure that allows us to account for incremental modifications to the source structure. Consider a change made to the left-hand-side (lhs) of assign. There is no need to rebuild the whole result term. One simply has to apply compile to the changed lhs and plug the result into the first operand of store.
store(compile(lhs), compile(rhs))
A homomorphic translation is allowed to perform a non-injective mapping, i.e., different elements of the source domain may be mapped onto the same element in the target domain. For instance, an initialization statement for a variable could be translated to a store instruction as well. In the target domain (machine language) there is no way to tell which type in the source domain caused the generation of a store (see section 2.7 Separation for further homomorphic simplifications).
Subsequently, we describe a general approach for homomorphic translations in the form of a design pattern [4]. The Translator pattern joins four aspects:
The last point is of technical nature but will immediately be explained within the core pattern description as it substantially influences the pattern's structure. We already elaborated on point one, i.e., assigning a meaning to a recursive structure in terms of a similarly structured recursive meaning. Point two refers to the fact that this recursive meaning is represented by an explicit intermediate data structure. Thus, the final semantics of the translation is given by a function on the intermediate data structure. This aspect, as well as point three, will be discussed in the pattern's consequences (section 2.7).