Using Cmm's parser in a Cmm programme

1 Introduction
2 Some implementation details

1 Introduction

Cmm represents C++ source as a parse tree whose nodes are each derived from a base Node class. Cmm outputs a parse tree as C++ text by calling the virtual Node::Output() method. Each node type overrides this virtual method to output appropriate text.

To output a parse tree in a different form, one could alter these virtual methods as required. However this would involve modifying the Cmm source code.

A different way would be to add a different virtual method to the base Node class, e.g. OutputCustom(), and provide implementations of this method in all the derived classes. This is not a very easy thing to do though - there are lots of different node classes.

A better solution is to use multimethods themselves. We rebuild Cmm's parser as a Cmm programme, and instead of outputing a parse tree using virtual methods, we output it using a multimethod. The crucial difference is that one can easily override multimethods without changing the original source code.

The multimethod in question has the following prototype:

        Output2( virtual const Parser::Node& node, virtual Parser::OutStream& out);

The default implementation of this multimethod looks like:

        Output2_( const Parser::Node& node, Parser::OutStream& out)
        {
                node.Output( out);
        }

-i.e. it simple calls the node's virtual Output() method.

One can now provide different implementations of the Output2() multimethod. For example, to customise the output of function bodies, one would supply the following function:

        void Output2_( const Parser::FnBody& fnbody, Parser::OutStream& out)
        {
                ...
        }

As a bonus, the second parameter in the multimethod is also virtual. This allows one to specialise on the output as well as the node type. For example, we can arange for specialised code to be called for all nodes, by deriving a new class from Parser::OutStream:

        struct CustomOutStream : Parser::OutStream {...};
        void        Output2_( const Parser::Node& node, CustomOutStream& out)
        {
                ...
        }

2 Some implementation details

Cmm's parser code has been modified to make the above system work. The Node::Output() method is never called directly - instead outputing a node is done by calling the global function Output2(). For example, most of the implementations of the virtual Node::Output() methods themselves need to output other nodes; they always do this by calling the Output2() function, never by calling a node's Output() method directly.

In normal builds of Cmm, Output2() is declared as a conventional C++ function, and it's implementation simply calls the node's virtual Output() method.

However, if CMM_MULTIMETHODS is defined, Output2() is declared as a multimethod function, and the implementation is renamed Output2_(), so that it acts as a multimethod implementation. Hence outputing a node always uses multimethod dispatch.

You can see this in Cmm's source code. ../cmm/parser.h declares Output2() as either a conventional function or a multimethod function, depending on whether CMM_MULTIMETHODS is defined.

Similarly, ../cmm/parser-nodemethods.cpp defines the Ouput2() or Output2_() functions depending on whether CMM_MULTIMETHODS is defined. It also has many different implementations of the Node::Output() method, which you will notice call Output2().