treebuild - command-line interface to Lang::Tree::Builder
$ treebuild [-o <dir>] [-p <prefix>] [-l <lang>] [<config>]
treebuild takes a configuration file, either from standard input or from the command line, and generates a set of classes from it. It is intended to make it easy to generate a large set of interrelated classes. My specific requirement when writing it was to have it create all the classes I would need to be able to construct abstract syntax trees.
treebuild
Although billed as a command line interface to Lang::Tree::Builder, you probably won't need to use the Lang::Tree::Builder API directly as it does nothing that this script can't tell it to do.
Lang::Tree::Builder
The generated classes are made useful by two features. Firstly an api is also generated which provides shorthands for the class constructors. Secondly the generated classes all support the Visitor pattern, and a base visitor class and/or interface is also generated as a convenience.
-o dir
Generate classes under dir. A common use of this would be -o lib.
dir
-o lib
-p prefix
Prepend the literal prefix to all generated class names. For example given a config containing ClassA, ClassB etc, a prefix of -p Foo:: would produce output classes Foo::ClassA etc, wheras -p Foo would produce FooClassA etc. Even if you're generating classes for a language other than Perl, you still need to use the Perl-style :: separator for namespace components.
prefix
ClassA
ClassB
-p Foo::
Foo::ClassA
-p Foo
FooClassA
::
-l lang
Generate classes for a language other than Perl. Currently supported values for lang are:
lang
To add support for other languages, look in the distribution for lib/Tree/Builder/Templates, create a new directory named after the target language in there, copy the templates from lib/Tree/Builder/Templates/Perl into it, and edit them appropriately. You may also need to add functionality to lib/Tree/Builder/Class.pm to support your templates. If you do add methods there, you should add them to Lang::Tree::Builder::Args too. Don't forget when you've finished to add the new language to the list of supported languages here!
lib/Tree/Builder/Templates
lib/Tree/Builder/Templates/Perl
lib/Tree/Builder/Class.pm
Lang::Tree::Builder::Args
See Template::Manual for details of the Template::Toolkit used to process the templates.
Template::Toolkit
The config file is free-form. Whitespace (including newlines) is not significant except to separate tokens. Comments run from a hash (#) to the end of the line. Each entry in the file has the same general form, best explained with some examples:
#
# a comment abstract Expr() abstract Expr Bool() Expr Op::Plus(Expr left, Expr right) Expr Op::Minus(Expr left, Expr right) Expr Op::Times(Expr left, Expr right) Expr Op::Divide(Expr left, Expr right) Expr Op::And(Expr, Expr) Expr Number(scalar value) ExprList(Expr, ExprList) ExprList EmptyExprList()
The above is saying:
abstract Expr()
there is an abstract class called Expr
Expr
abstract Expr Bool()
there is an abstract class called Bool that inherits from Expr.
Bool
Expr Op::Plus(Expr left, Expr right)
there is a concrete class called Op::Plus that inherits from Expr, and its constructor takes an Expr called left and an Expr called right as arguments.
Op::Plus
left
right
Expr Op::Minus(Expr left, Expr right)
Expr Op::Times(Expr left, Expr right)
Expr Op::Divide(Expr left, Expr right)
more of the same.
Expr Op::And(Expr, Expr)
there is a concrete class called Op::And that inherits from Expr, and its constructor takes two unnamed Expr as arguments.
Op::And
Expr Number(scalar value)
there is a concrete class called Number which inherits from Expr and its constructor takes a simple scalar called value as argument.
Number
value
ExprList(Expr, ExprList)
there is a concrete class called ExprList whose constructor takes an Expr and another ExprList as argument.
ExprList
ExprList EmptyExprList()
there is a concrete class called EmptyExprList which is an ExprList and its constructor takes no arguments.
EmptyExprList
Assume the above config exists in a file called example.tb, and that the following command is run:
example.tb
$ treebuild -o ./test -p Foo:: -l Perl example.tb
This will create the directory ./test, and inside there will create the following files:
./test
Foo/Expr.pm
Code implementing an abstract Foo::Expr class (because the config declared Expr to be abstract). Being abstract, this class has only a new() method, and that will die if ever it is called.
Foo::Expr
new()
die
Foo/Bool.pm
Code implementing an abstract Foo::Bool class which inherits from Expr. It also has a poisoned new() method.
Foo::Bool
Foo/Op/Plus.pm
Foo/Op/Minus.pm
Foo/Op/Times.pm
Foo/Op/Divide.pm
Code implementing a Foo::Op::Plus class, a Foo::Op::Minus class, etc. Each of these classes are declared to inherit from Foo::Expr. They each have a new() method that accepts two arguments. Both arguments to new() are required to inherit from Foo::Expr. These arguments correspond to the (Expr left, Expr right) components of the config. Each of the generated classes also provides two accessor methods: getLeft() and getRight() for retrieving the values, and an accept() method which supports the visitor pattern, see below.
Foo::Op::Plus
Foo::Op::Minus
(Expr left, Expr right)
getLeft()
getRight()
accept()
Foo/Op/And.pm
Code implementing a Foo::Op::And class. In this case, because the config did not name the arguments to the constructor but only the types, the fields will be named after the types of the arguments. Also, because these names are not unique, they will be suffixed by an incrementing number. Therefore the accessor methods will be called getExpr1() and getExpr2(). In other respects it is the same as the Foo::Op::Times class and its siblings.
Foo::Op::And
getExpr1()
getExpr2()
Foo::Op::Times
Foo/Number.pm
Code implementing a Foo::Number class. As specified in the config, it inherits from Expr and its constructor accepts a single simple scalar value (not a reference). It provides a single getValue() accessor (because the config stated (scalar value), and an accept() method.
Foo::Number
getValue()
(scalar value)
Foo/ExprList.pm
Code implementing a Foo::ExprList class. The class in this case does not inherit from Foo::Expr. Its new() method accepts another Foo::ExprList and a Foo::Expr as argument, and it has accessor methods getExprList() and getExpr(). As usual it also has an accept() method (more on that later).
Foo::ExprList
getExprList()
getExpr()
Foo/EmptyExprList.pm
Code implementing a Foo::EmptyExprList class. This class is defined to inherit from Foo:ExprList and its constructor takes no arguments. Likewise it has no accessor methods, but still provides an accept() method.
Foo::EmptyExprList
Foo:ExprList
Foo/API.pm
Code implementing a Foo::API class. This class performs a couple of functions. Firstly it includes (with use) all of the other classes generated so is a useful one-stop-shop for ensuring they are all loaded (well actually it doesn't bother use-ing the abstract classes, on the assumption that a concrete class will have loaded them). The second job it does is to provide a shorthand for each of the concrete classes constructors. In the case of our example, it will create subroutines Plus(), Minus(), Times(), Divide(), And(), Number(), ExprList() and EmptyExprList(). It puts all of these on its @EXPORT_OK list and makes them available via the shorthand :all import tag. That means you can write things like:
Foo::API
use
Plus()
Minus()
Times()
Divide()
And()
Number()
ExprList()
EmptyExprList()
@EXPORT_OK
:all
use Foo::API qw(:all); my $exprlist = ExprList(Plus(Number(2), Times(Number(3), Number(4))), ExprList(Number(5), EmptyExprList()));
rather than the much more verbose:
use Foo::API; my $exprlist = Foo::ExprList->new( Foo::Op::Plus->new( Foo::Number->new(2), Foo::Op::Times->new( Foo::Number->new(3), Foo::Number->new(4) ) ), Foo::ExprList->new( Foo::Number->new(5), Foo::EmptyExprList->new() ) );
Foo/Visitor.pm
Code implementing a Foo::Visitor class. As noted above, each of the concrete classes defined in the config will have an accept() method. That accept() method takes a $visitor as argument and calls a specific method on it, passing itself as argument. For example the accept() method of Foo::Op::Plus will look something like:
Foo::Visitor
$visitor
sub accept { my ($self, $visitor) = @_; $visitor->visitPlus($self); }
Similarily the ExprList's accept() will call visitExprList($self), etc.
visitExprList($self)
The Foo::Visitor class generated here provides default implementations for all of these methods. It also provides a default combine method that aggregates the results. It is intended that you inherit from this and override whatever you need to.
combine
Some effort has been made to generate extensive inline pod documentation for all generated classes, in some ways better than the documentation for the generator! I suggest copying the above config and running treebuild on it, as in the example, then running perldoc on the generated files.
Bill Hails <me@billhails.net>
Lang::Tree::Builder::Args, Lang::Tree::Builder::Class, Lang::Tree::Builder::Data, Lang::Tree::Builder::Parser, Lang::Tree::Builder::Scalar, Lang::Tree::Builder::Tokenizer and last but not least Lang::Tree::Builder itself.
To install Lang::Tree::Builder, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Lang::Tree::Builder
CPAN shell
perl -MCPAN -e shell install Lang::Tree::Builder
For more information on module installation, please visit the detailed CPAN module installation guide.