| Home | Downloads | Documentation | Plugins | Spinoff Projects | Mailing List |
Tutorial 6: Returning ListsIn this tutorial we will develop step-by-step a transform that expandsinclude statements. For example, if b.php is
<?php echo "Hello world"; ?> and a.php is <?php include "b.php"; echo "Goodbye!"; ?> Then running the transform on a.php yields <?php echo "Hello world\n"; echo "Goodbye\n"; ?> The transform we will develop in this tutorial is only a simple
implementation of Deleting Nodes Our transform should process
class Expand_includes : public Tree_transform
{
public:
AST_expr* pre_method_invocation(AST_method_invocation* in)
{
// Process includes
}
};
However, this will not get us very far. The return type of
Recall from Tutorial 1 that to
turn an expression into a statement, phc
inserts an void pre_eval_expr(AST_eval_expr* in, AST_statement_list* out) This is different from the signatures we have seen so far. For
nodes that can be replaced by a number of new nodes, the pre transform
and post transform methods will not have a return value in their
signature, but have an extra So, we will use the following plugin as our starting point.
Executing this plugin deletes all
#include <phc/Tree_transform.h>
class Expand_includes : public Tree_transform
{
public:
void pre_eval_expr(AST_eval_expr* in, AST_statement_list* out)
{
}
};
extern "C" void process_ast(AST_php_script* php_script)
{
Expand_includes einc;
php_script->transform(&einc);
}
Using the XML unparser So, we now want to do something more useful than deleting all
#include <phc/Tree_transform.h>
#include <phc/process_ast/XML_unparser.h>
class Expand_includes : public Tree_transform
{
private:
XML_unparser xml_unparser;
public:
void pre_eval_expr(AST_eval_expr* in, AST_statement_list* out)
{
in->visit(&xml_unparser);
}
}
The XML unparser is implemented using the
When you run this transform on a.php, it will print
two
<AST_eval_expr>
<AST_method_invocation>
<Token_class_name>
<value>%STDLIB%</value>
</Token_class_name>
<Token_method_name>
<value>include</value>
</Token_method_name>
<AST_actual_parameter_list>
<AST_actual_parameter>
<bool>false</bool>
<Token_string>
<value>b.php</value>
<source_rep>b.php</source_rep>
</Token_string>
</AST_actual_parameter>
</AST_actual_parameter_list>
</AST_method_invocation>
</AST_eval_expr>
This tells us that the
class Expand_includes : public Tree_transform
{
public:
void pre_eval_expr(AST_eval_expr* in, AST_statement_list* out)
{
// Pattern to match include statements
Token_string* filename;
AST_actual_parameter* param;
AST_actual_parameter_list* params;
Token_method_name* method_name;
Token_class_name* target;
AST_method_invocation* pattern;
filename = new Token_string(WILDCARD, WILDCARD);
param = new AST_actual_parameter(false, filename);
params = new AST_actual_parameter_list();
params->push_back(param);
method_name = new Token_method_name(new String("include"));
target = new Token_class_name(new String("%STDLIB%"));
pattern = new AST_method_invocation(target, method_name, params);
// Check we have a matching function
if(!in->expr->match(pattern))
{
// No match; leave untouched
out->push_back(in);
}
else
{
// Process the include
}
}
};
Note how the construction of the pattern follows the structure
of the tree as output by the XML unparser exactly. The only
difference is that we leave the actual filename a wildcard;
obviously, we want to be able to match against any
The Full Transform Remember from the previous tutorials that code defined outside
the scope of any class and any function becomes part of
#include <phc/Tree_transform.h>
#include <phc/parse.h>
class Expand_includes : public Tree_transform
{
public:
void pre_eval_expr(AST_eval_expr* in, AST_statement_list* out)
{
// Pattern to match include statements
Token_string* filename;
AST_actual_parameter* param;
AST_actual_parameter_list* params;
Token_method_name* method_name;
Token_class_name* target;
AST_method_invocation* pattern;
filename = new Token_string(WILDCARD, WILDCARD);
param = new AST_actual_parameter(false, filename);
params = new AST_actual_parameter_list();
params->push_back(param);
method_name = new Token_method_name(new String("include"));
target = new Token_class_name(new String("%STDLIB%"));
pattern = new AST_method_invocation(target, method_name, params);
// Check we have a matching function
if(!in->expr->match(pattern))
{
// No match; leave untouched
out->push_back(in);
}
else
{
// Try to open the file
AST_php_script* php_script = parse(filename->value, NULL, false);
if(php_script == NULL)
{
cout << "Could not parse file " << *filename->value;
cout << " on line " << in->get_line_number() << endl;
exit(-1);
}
// Replace the include by all statements in %MAIN%::%run%
AST_class_def* main = php_script->get_class_def("%MAIN%");
AST_method* run = main->get_method("%run%");
out->push_back_all(run->statements);
}
}
};
What's Next? This is the last tutorial in this series on using the
And of course, we are more than happy to answer any other questions you might still have. Just send an email to the mailing list and we'll do our best to answer you as quickly as possible! Happy coding! |
| $LastChangedDate: 2006-09-08 12:24:58 +0100 (Fri, 08 Sep 2006) $. Contents © the authors. |