Visitors
The syntax tree constructed by the parser is designed to be traversed
with visitors. The syntax tree can be traversed as it is being formed
(in the parser onNode function), or when the syntax tree
is fully constructed.
A visitor must derive from the generated base visitor class (or
another visitor). The base visitor class contains a virtual
visit function for each node defined in the grammar file.
The constructor of the class takes one bool argument. If the argument
is true, then the visitor is slippery. The argument is
defaulted to true.
For example, consider the grammar
# start rule
[ClassSpecNode]
start -> CLASS IDENT COLON ident-list-opt
# ident list opt
ident-list-opt -> ident-list
ident-list-opt ->
# ident list
ident-list -> ident
[$ ListNode]
ident-list -> ident-list COMMA ident
# ident
[IdentNode]
ident -> IDENT
The generated base visitor class is
// NodeVisitor.lzz
//
$hdr
#include "basl_Visitor.h"
$end
$src
#include "Nonterm.h"
$end
// node forward declarations
class ClassSpecNode;
class ListNode;
class IdentNode;
// base visitor
class NodeVisitor : public basl::Visitor
{
// true if slippery
bool m_slippery;
public:
// constructor
inline NodeVisitor (bool slippery = true)
: m_slippery (slippery)
{
}
// start -> CLASS IDENT COLON ident-list-opt
virtual void visit (ClassSpecNode & node) const
{
}
// ident-list -> ident-list COMMA ident
virtual void visit (ListNode & node) const
{
if (m_slippery)
{
node.visitChildren (* this);
}
}
// ident -> IDENT
virtual void visit (IdentNode & node) const
{
}
}
The visit functions take a non-const reference to a node.
This allows the visit functions to attach attributes to the nodes.
Visitors can be developed very easily with lzz. For example,
the visitor below collects the identifier lexemes in the above
grammar.
// get identifiers
struct GetIdentListVisitor (IdentVector & ident_set) : NodeVisitor (true)
{
// ident -> IDENT
void visit (IdentNode & node) const
{
ident_set.push_back (node.getIDENT ().getLexeme ());
}
}
This visitor could be called when visiting ClassSpecNode:
// my visitor
struct MyVisitor () : NodeVistor ()
{
// start -> CLASS IDENT COLON ident-list-opt
void visit (ClassSpecNode & node) const
{
IdentVector ident_set;
node.getIdentListOpt ().accept (GetIdentListVisitor (ident_set));
}
}
The member functions of the generated nonterminals that return the
right-hand side nodes are named after the corresponding symbols. For
a token, the get function is named "get" followed by the name of the
token (in upper case), with all '-' characters substituted with '_'.
For a nonterminal, the get function is named "get" followed by the
name of the symbol, with all '-' and '_' characters removed and the
words separated by those characters capitalized. The table
below contains some examples.
| Symbol name | Generated get function name |
ident | getIdent |
ident-list | getIdentList |
ident_list | getIdentList |
TIMES | getTIMES |
TIMES-TIMES | getTIMES_TIMES |
TIMES_TIMES | getTIMES_TIMES |
A number starting with 1 is appended to any names that are the same.
A get function will return one of three types:
-
basl::Token
-
This is the return type if the symbol is a token, or if the symbol is
a nonterminal and it can represent only tokens. For example, in
StartNode, defined in the grammar below, the functions
getA and getB will have the return the type
basl::Token.
[StartNode] start -> A b
b ->
b -> B
b -> BB
The function basl::Token::isSet () returns true if
a token is set.
-
basl::Nonterm
-
This is the return type if the symbol is a nonterminal and it can
represent only nonterminal nodes, or the nonterminal can only be
epsilon. For example, in
StartNode, defined in the
grammar below, the functions getA and getB
will have the return type basl::Nonterm.
[StartNode] start -> a b
a ->
[A1Node] a -> A
[A2Node] a -> A A
b ->
The function basl::Nonterm::isSet () returns true if
a nonterminal is set.
-
basl::Node
-
This is the return type otherwise. This type encapsulates both a
token and a nonterminal. The functions
basl::Node::getToken
() and basl::Node::getNonterm () return a node's
token and nonterminal nodes respectively.
The basl::Nonterm::accept function accepts a visitor.
This function initiates the double dispatch mechanism on a
nonterminal, but only if the nonterminal is set.
Attributes can be attached to nonterminal nodes only. The functions
basl::Nonterm::setAttrib (AttribPtr const &) and
basl::Nonterm::getAttrib () can be used to set and get
the attribute of a nonterminal node respectively.
basl::Attrib is defined in basl_Attrib.lzz:
// basl_Attrib.lzz
//
$hdr
$end
$src
$end
// basil
namespace basl
{
// Attrib
class Attrib
{
public:
// constructor
Attrib ()
{
}
// destructor
virtual ~ Attrib ()
{
}
}
}
The type basl::AttribPtr is a typedef to reference
counted smart pointer.
|