next up previous
Next: Related Patterns Up: Design Pattern: Translator Previous: Implementation

   
Sample Code

In the following we present Eiffel code since it is very readable and Eiffel features garbage collection. However, you may use Translator with any language featuring runtime type information such as Smalltalk, C++, and Java.

Assume a toy source language with an if-statement (see figure 3):

class    TOY_IFTHEN
inherit  TOY_LANG
creation make
feature  
exp, stat:  TOY_LANG;
...
end

The corresponding pretty-print element could be:

class    PP_IFTHEN
inherit  PP_LANG
creation make
feature  
pexp, pstat:  PP_LANG;

make (e, s : PP_LANG) is 
  do
    pexp:=e;
    pstat:=s; 
  end
 
display is
  do
    io.putstring ("IF "); 
    pexp.display 
    io.putstring (" THEN ");
    pstat.display;
    io.putstring (" END")
    io.new_line;
   end;

Now we need the specialized function that maps an if-statement to its pretty-print element.

class PRETTY_FUNCTION_IFTHEN
inherit FUNCTION[TOY_IFTHEN, PP_IFTHEN]
creation make
feature 
genFunc: GEN_FUNC[TOY_LANG, PP_LANG];

make(s: GEN_FUNC[TOY_LANG, PP_LANG]) is
  do
    genFunc:=s
  end;
  infix "@"(ift: TOY_IFTHEN) : 
        PP_IFTHEN is
  do
    !PP_IFTHEN!Result.make 
      (genFunc @ ift.exp, 
       genFunc @ ift.stat)
  end;
end
The creation argument of type GEN_FUNC specifies the generic function to be used for evaluation of subcomponents (exp and stat). Its generic4 parameters denote the function type to be going from TOY_LANG to PP_LANG.

The method for function application (@) simply creates the pretty-print element while supplying the results of recursively evaluating the subcomponents (exp and stat).

The client code for performing a full interpretation is:

source:           TOY_LANG;
pp_structure:     PP_LANG;
pretty_functions: PP_FUNCTIONS;
prettyPrint:      GEN_FUNC[TOY_LANG, 
                           PP_LANG];
...
!!pretty_functions.init;
!!prettyPrint.make (pretty_functions);

pp_structure:=prettyPrint @ source;
pp_structure.display;
...

Prior to its usage, a function package must be initialized by calling init. Then, a generic function (prettyPrint) is created by suppling a pretty-print function package (pretty_functions)5. Next, the generic function is applied to the source structure, yielding a target structure (pp_structure). The semantics are finally produced by invoking (display) on the target structure.

A concrete function package appears as follows:

class    PP_FUNCTIONS
inherit  FUNCTIONS[TOY_LANG, PP_LANG]
creation init
feature  
init is
  local  
    pf_var:      PF_VAR;
    pf_assign:   PF_ASSIGN;
    pf_ifthen:   PF_IFTHEN;
    prettyPrint: GEN_FUNC[TOY_LANG, 
                          PP_LANG]
  do
    make(3);
    !!prettyPrint.make(Current);
    !!pf_var;
    !!pf_assign.make(prettyPrint);
    !!pf_ifthen.make(prettyPrint);
    put(pf_var,    "TOY_VAR");
    put(pf_assign, "TOY_ASSIGN");
    put(pf_ifthen, "TOY_IFTHEN")
  end;
end
Each concrete package inherits from an abstract function package class which, in turn, inherits from HASH_TABLE:
deferred 
class FUNCTIONS[SOURCE, TARGET]
inherit HASH_TABLE[
          FUNCTION[SOURCE, TARGET], 
          STRING]
feature  
init is deferred end;
end

So, make(3) initializes the function package to a hash table with three entries. Next, a generic function is created in order to serve as the creation argument for the three specialized function prototypes. The function to print variables (pf_var) does not need to recursively evaluate subcomponents, ergo it does not require a generic function for its creation. Note that the Current argument in the creation of the generic function causes the very function package that is currently being initialized to become the argument for the generic function that is supplied to the specialized functions. Finally, the specialized function prototypes are put into the hash table using their corresponding source element class names as keys.

Therefore, the application method of the generic function definition --

class    GEN_FUNC[SOURCE, TARGET]
inherit  FUNCTION[SOURCE, TARGET];
         INTERNAL;
creation make
feature  
functions: FUNCTIONS [SOURCE, TARGET];

make (fs: like functions) is
  do
    functions:=fs
  end;
        
infix "@" (source: SOURCE): TARGET is
  do
    Result:= 
      clone(functions.item
             (class_name(source))
           ) @ source
  end;
end
-- can simply access the class name of the source element (method class_name is inherited from the system class INTERNAL), use it to retrieve the correct specialized function prototype (call item on the function package)6, and then apply a cloned exemplar to its own argument. Instead of a hash table we also could have used a dictionary or even a type case switching statement in order to achieve dispatching on the type of arguments.
next up previous
Next: Related Patterns Up: Design Pattern: Translator Previous: Implementation
Thomas Kuehne
1998-07-06