File Coverage

blib/lib/Lang/Tree/Builder.pm
Criterion Covered Total %
statement 34 118 28.8
branch 4 18 22.2
condition n/a
subroutine 8 29 27.5
pod 2 19 10.5
total 48 184 26.0


line stmt bran cond sub pod time code
1             package Lang::Tree::Builder;
2              
3 2     2   48806 use strict;
  2         4  
  2         88  
4 2     2   10 use warnings;
  2         4  
  2         63  
5              
6 2     2   1286 use Lang::Tree::Builder::Parser;
  2         6  
  2         66  
7 2     2   3433 use Template;
  2         70497  
  2         67  
8 2     2   24 use FileHandle;
  2         5  
  2         79  
9              
10             our $VERSION = '0.1';
11              
12             =head1 NAME
13              
14             Lang::Tree::Builder - Build Classes from a Tree Definition File
15              
16             =head1 SYNOPSIS
17              
18             use Lang::Tree::Builder;
19             my $builder = new Lang::Tree::Builder(%params);
20             $builder->build($config_file);
21              
22             =head1 DESCRIPTION
23              
24             C takes a set of parameters and a tree definition file
25             and uses L to generate perl classes and an API.
26              
27             It is intended primarily to take the drudgery out of writing all of
28             the support classes needed to build abstract syntax trees.
29              
30             =head2 new
31              
32             my $builder = new Lang::Tree::Builder(%params);
33              
34             C<%params> are
35              
36             =over 4
37              
38             =item dir
39              
40             The output directory, default C<./>
41              
42             =item prefix
43              
44             A prefix class prepended to all generated classes, default none.
45             If a prefix is provided, it is used literally. i.e. if you want
46             C you must supply the C<::> explicitly.
47              
48             =item lang
49              
50             The language of the generated classes.
51              
52             =back
53              
54             =cut
55              
56             sub new {
57 2     2 1 33 my ($class, %params) = @_;
58              
59 2         13 my %defaults = (
60             dir => '.',
61             prefix => '',
62             lang => 'Perl',
63             );
64              
65 2         9 foreach my $key (keys %params) {
66 4 50       14 die "unrecognised param: $key" unless exists $defaults{$key};
67             }
68              
69 2         6 my %body;
70              
71 2         7 foreach my $key (keys %defaults) {
72 6 100       21 $body{$key} =
73             exists($params{$key})
74             ? $params{$key}
75             : $defaults{$key};
76             }
77              
78 2         16 my $self = bless {%body}, $class;
79 2         10 $self->{includes} = $self->findIncludes();
80 2         12 $self->{ext} = $self->getExt();
81 0         0 $self->initVmethods();
82              
83             # add to this hash for new kinds of targets
84 0         0 $self->{TEMPLATES} = {
85             visitor => 'Visitor',
86             cvis => 'CopyingVisitor',
87             printer => 'Printer',
88             xml => 'XML',
89             api => 'API',
90             };
91              
92 0         0 return $self;
93             }
94              
95             {
96             my $init = 0;
97              
98             sub initVmethods {
99 0 0   0 0 0 unless ($init) {
100             $Template::Stash::SCALAR_OPS->{camelcase} = sub {
101 0     0   0 my ($text) = @_;
102 0         0 $text =~ s/_(\w)/uc($1)/eg;
  0         0  
103 0         0 ucfirst($text);
104 0         0 };
105              
106             $Template::Stash::SCALAR_OPS->{uncamel} = sub {
107 0     0   0 my ($text) = @_;
108 0         0 $text =~ s/([A-Z])/'_' . lc($1)/eg;
  0         0  
109 0         0 $text =~ s/^_//;
110 0         0 $text;
111 0         0 };
112              
113             $Template::Stash::SCALAR_OPS->{tolower} = sub {
114 0     0   0 my ($text) = @_;
115 0         0 lc($text);
116 0         0 };
117              
118             $Template::Stash::SCALAR_OPS->{getMethod} = sub {
119 0     0   0 my ($text) = @_;
120 0         0 $text =~ s/_(\w)/uc($1)/eg;
  0         0  
121 0         0 'get' . ucfirst($text);
122 0         0 };
123              
124             $Template::Stash::LIST_OPS->{sz} = sub {
125 0     0   0 my ($list) = @_;
126 0         0 scalar(@$list);
127 0         0 };
128              
129 0         0 $init = 1;
130             }
131             }
132             }
133              
134             =head2 build
135              
136             $builder->build($config_file);
137              
138             Builds the tree classes, API and Visitor from definitions
139             in the C<$config_file>.
140              
141             =cut
142              
143             sub build {
144 0     0 1 0 my ($self, $config_file) = @_;
145              
146 0         0 my $data = $self->getData($config_file);
147 0         0 $self->buildClasses($data, $config_file);
148 0         0 $self->buildSpecialClasses($data, $config_file);
149             }
150              
151             # parses the $config file and returns its $data
152             sub getData {
153 0     0 0 0 my ($self, $config_file) = @_;
154              
155 0         0 my $parser = Lang::Tree::Builder::Parser->new(prefix => $self->{prefix});
156 0         0 return $parser->parseFile($config_file);
157             }
158              
159             # build each class file from the $data
160             sub buildClasses {
161 0     0 0 0 my ($self, $data, $config_file) = @_;
162              
163 0         0 foreach my $class ($data->classes) {
164 0         0 $self->buildClass($data, $config_file, $class);
165             }
166             }
167              
168             # build an individual class file
169             sub buildClass {
170 0     0 0 0 my ($self, $data, $config_file, $class) = @_;
171              
172 0         0 $self->runTemplate($data, $config_file, 'Class.tt2', $class,
173             class => $class);
174             }
175              
176             sub buildSpecialClasses {
177 0     0 0 0 my ($self, $data, $config_file) = @_;
178 0         0 foreach my $key (keys %{$self->{TEMPLATES}}) {
  0         0  
179 0         0 $self->buildSpecialClass($data, $config_file, $self->{TEMPLATES}{$key});
180             }
181             }
182              
183             sub buildSpecialClass {
184 0     0 0 0 my ($self, $data, $config_file, $template) = @_;
185 0         0 $self->runTemplate($data, $config_file, "$template.tt2",
186             $self->makeSpecialClass($template));
187             }
188              
189             sub runTemplate {
190 0     0 0 0 my ($self, $data, $config_file, $input_template, $output_class, %extra)
191             = @_;
192              
193 0 0       0 unless ($self->findTemplate($input_template)) {
194 0         0 warn "templates $input_template not found\n";
195 0         0 return;
196             }
197 0         0 my $template = $self->makeTemplate();
198 0 0       0 $template->process(
199             $input_template,
200             $self->makeVars($data, $config_file, %extra),
201             $self->getOutputFilename($output_class)
202             ) or die "builder failed: ", $template->error();
203             }
204              
205             # create the vars hash for the template
206             sub makeVars {
207 0     0 0 0 my ($self, $data, $config_file, %extra) = @_;
208              
209             return {
210 0         0 warning => $self->makeWarning($config_file),
211             data => $data,
212 0         0 %{$self->makeSpecialClasses()},
213             ext => $self->{ext},
214             %extra
215             };
216             }
217              
218             sub makeSpecialClasses {
219 0     0 0 0 my ($self) = @_;
220 0         0 my $classes = {};
221 0         0 foreach my $key (keys %{$self->{TEMPLATES}}) {
  0         0  
222 0         0 $classes->{$key} = $self->makeSpecialClass($self->{TEMPLATES}->{$key});
223             }
224 0         0 return $classes;
225             }
226              
227             sub makeSpecialClass {
228 0     0 0 0 my ($self, $class) = @_;
229 0         0 new Lang::Tree::Builder::Class(class => ($self->{prefix} . $class));
230             }
231              
232             # create a new Template object
233             sub makeTemplate {
234 0     0 0 0 my ($self) = @_;
235              
236 0 0       0 Template->new($self->makeTemplateConfig())
237             || die "$Template::ERROR\n";
238             }
239              
240             # returns the warning message
241             sub makeWarning {
242 0     0 0 0 my ($self, $config_file) = @_;
243              
244 0         0 "Automatically generated from $config_file on "
245             . scalar(gmtime)
246             . " GMT.";
247             }
248              
249             # returns a hashref of config for the Template
250             sub makeTemplateConfig {
251 0     0 0 0 my ($self) = @_;
252              
253             return {
254 0         0 INCLUDE_PATH => $self->{includes},
255             INTERPOLATE => 0,
256             POST_CHOMP => 0,
257             EVAL_PERL => 0,
258             };
259             }
260              
261             sub findTemplate {
262 0     0 0 0 my ($self, $template) = @_;
263 0         0 foreach my $inc (@{ $self->{includes} }) {
  0         0  
264 0 0       0 return "$inc/$template" if -f "$inc/$template";
265             }
266 0         0 return '';
267             }
268              
269             # returns the filename extension for all generated files
270             sub getExt {
271 2     2 0 4 my ($self) = @_;
272              
273 2         5 foreach my $inc (@{ $self->{includes} }) {
  2         7  
274 22         43 my $extfile = "$inc/EXT";
275 22 50       665 if (-e $extfile) {
276 0         0 my $fh = new FileHandle($extfile);
277 0 0       0 die "$extfile: $@" unless $fh;
278 0         0 my $ext = '';
279 0         0 while (defined(my $line = $fh->getline())) {
280 0         0 chomp($line);
281 0         0 $line =~ s/#.*//;
282 0         0 $line =~ s/\s+//g;
283 0         0 $ext .= $line;
284             }
285 0         0 return $ext;
286             }
287             }
288              
289 2         553 die "can't find EXT";
290             }
291              
292             # returns the output filename for an argument Lang::Tree::Builder::Class
293             sub getOutputFilename {
294 0     0 0 0 my ($self, $class) = @_;
295              
296 0         0 return $self->{dir} . '/' . $class->name('/') . $self->{ext};
297             }
298              
299             # returns an arrayref of locations to search for Templates
300             sub findIncludes {
301 2     2 0 6 my ($self) = @_;
302              
303 2         7 [ map { "$_/Tree/Builder/Templates/$self->{lang}" } @INC ];
  22         91  
304             }
305              
306             =head1 AUTHOR
307              
308             Bill Hails
309              
310             =head1 SEE ALSO
311              
312             Full documentation is in the command line interface L.
313              
314             =cut
315              
316             1;