NAME
treebuild - command-line interface to Lang::Tree::Builder
SYNOPSIS
$ 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.
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.
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.
Options
-o dir
-
Generate classes under
dir
. A common use of this would be-o lib
. -p prefix
-
Prepend the literal
prefix
to all generated class names. For example given a config containingClassA
,ClassB
etc, a prefix of-p Foo::
would produce output classesFoo::ClassA
etc, wheras-p Foo
would produceFooClassA
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. -l lang
-
Generate classes for a language other than Perl. Currently supported values for
lang
are:- Perl
- PHP
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 fromlib/Tree/Builder/Templates/Perl
into it, and edit them appropriately. You may also need to add functionality tolib/Tree/Builder/Class.pm
to support your templates. If you do add methods there, you should add them toLang::Tree::Builder::Args
too. Don't forget when you've finished to add the new language to the list of supported languages here!See Template::Manual for details of the
Template::Toolkit
used to process the templates.
Format of the config file
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
abstract Expr Bool()
-
there is an abstract class called
Bool
that inherits fromExpr
. Expr Op::Plus(Expr left, Expr right)
-
there is a concrete class called
Op::Plus
that inherits fromExpr
, and its constructor takes anExpr
calledleft
and anExpr
calledright
as arguments. 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 fromExpr
, and its constructor takes two unnamedExpr
as arguments. Expr Number(scalar value)
-
there is a concrete class called
Number
which inherits fromExpr
and its constructor takes a simple scalar calledvalue
as argument. ExprList(Expr, ExprList)
-
there is a concrete class called
ExprList
whose constructor takes anExpr
and anotherExprList
as argument. ExprList EmptyExprList()
-
there is a concrete class called
EmptyExprList
which is anExprList
and its constructor takes no arguments.
Output Files
Assume the above config exists in a file called example.tb
, and that the following command is run:
$ treebuild -o ./test -p Foo:: -l Perl example.tb
This will create the directory ./test
, and inside there will create the following files:
Foo/Expr.pm
-
Code implementing an abstract
Foo::Expr
class (because the config declaredExpr
to be abstract). Being abstract, this class has only anew()
method, and that willdie
if ever it is called. Foo/Bool.pm
-
Code implementing an abstract
Foo::Bool
class which inherits fromExpr
. It also has a poisonednew()
method. Foo/Op/Plus.pm
Foo/Op/Minus.pm
Foo/Op/Times.pm
Foo/Op/Divide.pm
-
Code implementing a
Foo::Op::Plus
class, aFoo::Op::Minus
class, etc. Each of these classes are declared to inherit fromFoo::Expr
. They each have anew()
method that accepts two arguments. Both arguments tonew()
are required to inherit fromFoo::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()
andgetRight()
for retrieving the values, and anaccept()
method which supports the visitor pattern, see below. 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 calledgetExpr1()
andgetExpr2()
. In other respects it is the same as theFoo::Op::Times
class and its siblings. Foo/Number.pm
-
Code implementing a
Foo::Number
class. As specified in the config, it inherits fromExpr
and its constructor accepts a single simple scalar value (not a reference). It provides a singlegetValue()
accessor (because the config stated(scalar value)
, and anaccept()
method. Foo/ExprList.pm
-
Code implementing a
Foo::ExprList
class. The class in this case does not inherit fromFoo::Expr
. Itsnew()
method accepts anotherFoo::ExprList
and aFoo::Expr
as argument, and it has accessor methodsgetExprList()
andgetExpr()
. As usual it also has anaccept()
method (more on that later). Foo/EmptyExprList.pm
class.-
Code implementing a
Foo::EmptyExprList
class. This class is defined to inherit fromFoo:ExprList
and its constructor takes no arguments. Likewise it has no accessor methods, but still provides anaccept()
method. Foo/API.pm
-
Code implementing a
Foo::API
class. This class performs a couple of functions. Firstly it includes (withuse
) all of the other classes generated so is a useful one-stop-shop for ensuring they are all loaded (well actually it doesn't botheruse
-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 subroutinesPlus()
,Minus()
,Times()
,Divide()
,And()
,Number()
,ExprList()
andEmptyExprList()
. 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: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 anaccept()
method. Thataccept()
method takes a$visitor
as argument and calls a specific method on it, passing itself as argument. For example theaccept()
method ofFoo::Op::Plus
will look something like:sub accept { my ($self, $visitor) = @_; $visitor->visitPlus($self); }
Similarily the
ExprList
'saccept()
will callvisitExprList($self)
, etc.The
Foo::Visitor
class generated here provides default implementations for all of these methods. It also provides a defaultcombine
method that aggregates the results. It is intended that you inherit from this and override whatever you need to.
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.
AUTHOR
Bill Hails <me@billhails.net>
SEE ALSO
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.