| Home | Downloads | Documentation | Plugins | Spinoff Projects | Mailing List |
Tutorial 1: Traversing the TreeIn Getting Started, we explained that phc represents PHP scripts internally as an abstract syntax tree, and that the structure of this tree is determined by the grammar. We then showed how to make use of this tree to count the number of classes. In this tutorial, we will consider an equally simple task: we want to count the number of function calls in a script. So, for the following PHP script, <?php echo "Hello "; echo "world!"; ?> we should report two function calls. Note that all the plugins that we will develop in these tutorials are included in the phc distribution. For example, in this tutorial we will be developing two plugins: a difficult solution to the problem and an easy solution to the problem. You can run these plugins by running phc --run plugins/tutorials/count_function_calls_difficult.so hello.phpor phc --run plugins/tutorials/count_function_calls_easy.so hello.php The Grammar (Revisited)How do we go about counting the number of function calls in a script? Remember that, as far as phc is concerned, a PHP script consists of a number of classes (and interface definitions). Each of these classes may have one or more methods, and each method can have one or more statements in them. Simplified, the grammar would state this as: php_script ::= class_def+ ; class_def ::= CLASS_NAME member* ; member ::= method | attribute ; method ::= signature statement* ; signature ::= METHOD_NAME formal_parameter* ; where the vertical bar (
(Note that this tree is simplified from the real tree; not all nodes are shown. You can also view the full tree). Statements and Expressions The two nodes that we are interested in are the “method
invocation” nodes. The The difference between statements and expressions is that a
statement does something (for example, a Now, the node statement ::= ... | eval_expr ; eval_expr ::= expr ; The Difficult SolutionThe following plugin counts the number of function calls in a tree. If you do not understand the code, do not worry! We will look at a much easier solution in a second. If you understand the comments, that is enough.
#include <phc/ast.h>
extern "C" void process_ast(AST_php_script* php_script)
{
AST_class_def_list::const_iterator ci;
AST_member_list::const_iterator mi;
AST_statement_list::const_iterator si;
AST_method* method;
AST_eval_expr* eval_expr;
AST_method_invocation* invoc;
int num_function_calls = 0;
// Inspect all classes
for(
ci = php_script->class_defs->begin();
ci != php_script->class_defs->end();
ci++)
{
// Inspect all members in the class
for(
mi = (*ci)->members->begin();
mi != (*ci)->members->end();
mi++)
{
// Check whether this member is a method or an attribute
method = dynamic_cast<AST_method*>(*mi);
if(method == NULL) continue;
// Check all statements in the method
for(
si = method->statements->begin();
si != method->statements->end();
si++)
{
// Check if the statement is of type "eval_expr"
eval_expr = dynamic_cast<AST_eval_expr*>(*si);
if(eval_expr == NULL) continue;
// Finally, check if the expression is a function call
invoc = dynamic_cast<AST_method_invocation*>(eval_expr->expr);
if(invoc == NULL) continue;
// Yeah! We found a function call
num_function_calls++;
}
}
}
printf("%d function calls found\n", num_function_calls);
}
Why is this code so complicated? First of all, it has to search
through the entire tree, looking for function calls. Because function
calls are fairly deep down in the tree, we need a lot of code simply
to find them. The second complication is the fact that, for example, a
class consists of “members“. A member is either an
attribute or a method, but we are interested only in methods.
Similarly, a method consists of “statements“. A statement
can be one of many things; but we are only interested in
The Easy Solution Fortunately, phc will do all this for you
automatically! There is a standard “do-nothing” tree
traversal predefined in phc in the form of a
class called In fact, there are two methods defined for each type of
node. The first method, called
#include <phc/Tree_visitor.h>
class Count_function_calls : public Tree_visitor
{
private:
int num_function_calls;
public:
// Set num_function_calls to zero before we begin
void pre_php_script(AST_php_script* in)
{
num_function_calls = 0;
}
// Print the number of function calls when we are done
void post_php_script(AST_php_script* in)
{
printf("%d function calls found\n", num_function_calls);
}
// Count the number of function calls
void post_method_invocation(AST_method_invocation* in)
{
num_function_calls++;
}
};
extern "C" void process_ast(AST_php_script* php_script)
{
Count_function_calls cfc;
php_script->visit(&cfc);
}
The real work in this transform is now done by the visitor; the
only task left that Counting All Statements(The plugin explained in this section is available as plugins/tutorials/count_statements.so in the phc distribution.) Suppose we wanted to count all statements, rather than just
function calls. We could define methods for statement ::= if | ... ; commented_node ::= ... | statement | ... ; node ::= ... | node | ... ; You could read this as “an if-statement is-a
statement, a statement is-a commented node (a node that may
have comments associated with it), and a commented node is-a
node. This is-a relation is reflection using inheritance (class
void pre_statement(AST_statement* in)
{
num_statements++;
}
We need to be precise about the order in which phc calls all these methods. Suppose we have a node
Just to emphasise, if all of the visitor methods listed above
are implemented, they will all be invoked, in the order
listed above. So, implementing a more specific visitor
( What's Next?To find out how you can modify the tree, continue with Tutorial 2. |
| $LastChangedDate: 2006-09-08 12:24:58 +0100 (Fri, 08 Sep 2006) $. Contents © the authors. |