The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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 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.

-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 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!

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 from Expr.

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.

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.

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.

ExprList(Expr, ExprList)

there is a concrete class called ExprList whose constructor takes an Expr and another ExprList as argument.

ExprList EmptyExprList()

there is a concrete class called EmptyExprList which is an ExprList 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 declared Expr to be abstract). Being abstract, this class has only a new() method, and that will die if ever it is called.

Foo/Bool.pm

Code implementing an abstract Foo::Bool class which inherits from Expr. It also has a poisoned new() method.

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/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/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/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/EmptyExprList.pm class.

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/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:

  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:

  sub accept {
      my ($self, $visitor) = @_;
      $visitor->visitPlus($self);
  }

Similarily the ExprList's accept() will call visitExprList($self), etc.

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.

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.