File Coverage

blib/lib/Text/PSP/Parser.pm
Criterion Covered Total %
statement 157 186 84.4
branch 59 88 67.0
condition 2 3 66.6
subroutine 15 16 93.7
pod 0 14 0.0
total 233 307 75.9


line stmt bran cond sub pod time code
1             package Text::PSP::Parser;
2 5     5   32 use Carp qw(croak);
  5         9  
  5         345  
3 5     5   26 use strict;
  5         11  
  5         14380  
4              
5             sub new {
6 14     14 0 31 my ($class,$engine) = @_;
7 14 50       50 ref $engine or die "No engine specified";
8 14         150 return bless {
9             engine => $engine,
10             },$class ;
11             }
12              
13             sub clone {
14 6     6 0 10 my ($self) = @_;
15 6         32 return bless {
16             engine => $self->{engine},
17             },ref $self;
18             }
19            
20              
21             sub parse_template {
22 20     20 0 42 my $self = shift;
23 20         174 %$self =
24             (
25             %$self,
26             @_,
27             );
28 20         96 $self->{head} = ['package ',$self->{classname},';
29             use strict;
30             use Text::PSP::Template;
31             use vars qw(@ISA);
32             @ISA = qw(Text::PSP::Template);
33              
34             # this file was generated by ',ref($self),'
35             '];
36 20 100       665 unless (defined $self->{directory}) {
37 16         28 my $directory = $self->{filename};
38 16         706 $directory =~ s/[^\/]+$//;
39 16         41 $self->{directory} = $directory;
40             }
41 20         602 $self->{out} = ["\n#line 1 $self->{engine}->{template_root}/$self->{filename}\n"];
42 20         65 $self->{define} = [];
43 20         38 $self->{pushing} = 0;
44 20         30 $self->{in_quotes} = 0;
45 20         373 local $_ = readline $self->{input};
46 20 50       70 if ($self->text) {
47 19         78 return $self->{head},$self->{define},$self->{out};
48             }
49 0         0 die "Parse error; unexpected end of file at $self->{filename} line $..\n";
50             }
51              
52             sub text {
53 44     44 0 62 my ($self) = @_;
54 44         56 my @text;
55             my ($switch);
56             FIND_TAG:
57 44         104 while (defined $_) {
58 66 100       253 if (s/^((.*?)<(%[!=@|]?))//s) {
59 25         61 $switch = $3; # before they go out of scope
60 25 100 66     161 push @text,$2 if defined $2 and $2 ne '';
61 25         60 last FIND_TAG;
62             }
63 41         72 push @text,$_;
64 41         214 $_ = readline $self->{input};
65             }
66 44 100       95 if (@text) {
67 34 100       119 push @{$self->{out}},'push @o' unless $self->{pushing};
  22         53  
68 34         49 $self->{pushing} = 1;
69 34 100       69 push @{$self->{out}},",'" unless $self->{in_quotes};
  31         57  
70 34         62 $self->{in_quotes} = 1;
71 34         42 push @{$self->{out}},map { s#\\#\\\\#g; s#'#\\'#g; $_ } @text;
  34         61  
  49         65  
  49         53  
  49         125  
72             }
73 44 100       108 unless (defined $_) {
74 19 50       52 push @{$self->{out}},"'" if $self->{in_quotes};
  19         43  
75 19 50       41 push @{$self->{out}},";" if $self->{pushing};
  19         37  
76 19         64 return 1;
77             }
78              
79 25 50       47 die "Parse error at $self->{engine}->{template_root}/$self->{filename} line $..\n" unless defined $switch;
80              
81 25 100       54 if ($switch eq '%=') {
82 10 100       24 push @{$self->{out}},"'" if $self->{in_quotes};
  8         19  
83 10         16 $self->{in_quotes} = 0;
84 10 100       24 push @{$self->{out}},'push @o' unless $self->{pushing};
  1         3  
85 10         14 $self->{pushing} = 1;
86 10         11 push @{$self->{out}},',',$self->get_block;
  10         27  
87 10         36 goto &text;
88             }
89 15 100       64 if ( $switch eq '%|' ) {
    100          
    100          
90 3 50       7 push @{$self->{out}},"'" unless $self->{in_quotes};
  0         0  
91 3 50       15 push @{$self->{out}},'push @o' unless $self->{pushing};
  0         0  
92 3         6 $self->{pusing} =1;
93 3         3 $self->{in_quotes} = 1;
94 3         10 goto &runnow;
95             }
96            
97             elsif ($switch eq '%!') {
98 1         4 goto &define;
99             }
100             elsif ($switch eq '%@') {
101 6         31 goto &directive;
102             }
103 5 100       18 push @{$self->{out}},"'" if $self->{in_quotes};
  2         6  
104 5 100       13 push @{$self->{out}},';' if $self->{pushing};
  2         4  
105 5         5 $self->{pushing} = 0;
106 5         7 $self->{in_quotes} = 0;
107 5 50       30 goto &code if $switch eq '%';
108 0         0 die "Parse error: unrecognized switch at $self->{engine}->{template_root}/$self->{filename} line $..\n";
109             }
110              
111             sub get_block {
112 25     25 0 35 my ($self) = @_;
113 25         30 my $block;
114 25         28 while (1) {
115 33         66 my $pos = index $_,'%>';
116 33 100       74 if ($pos == -1) {
117 9         13 $block .= $_;
118 9 100       56 defined ($_ = readline $self->{input}) or die "End of file in code-block at $self->{engine}->{template_root}/$self->{filename} line $..\n";
119 8         9 next;
120             }
121 24         64 $block .= substr($_,0,$pos,'');
122 24         32 substr($_,0,2,'');
123 24         274 return $block;
124             }
125              
126             }
127              
128             sub set_line {
129 6     6 0 10 my $self = shift;
130 6 50       14 if ($self->{'pushing'}) {
131 0         0 push @{$self->{out}},";";
  0         0  
132 0         0 $self->{'pushing'} = 0;
133             }
134 6         8 push @{$self->{out}},"\n#line $. $self->{engine}->{template_root}/$self->{filename}\n";
  6         177  
135             }
136              
137              
138             sub define {
139 1     1 0 2 my ($self) = @_;
140 1         1 push @{$self->{define}},"\n#line $. $self->{engine}->{template_root}/$self->{filename}\n",$self->get_block;
  1         11  
141 1         3 goto &text;
142             }
143              
144             sub code {
145 5     5 0 8 my ($self) = @_;
146 5         6 push @{$self->{out}},$self->get_block;
  5         16  
147 4         10 goto &text;
148             }
149              
150             sub directive {
151 6     6 0 7 my ($self) = @_;
152 6         15 my $directive = $self->get_block;
153 6 50       36 if ($directive =~ s/^\s*(\w+)\s+//s) {
154 6         16 my $name = $1;
155 6         8 my @args;
156 6         37 while ($directive =~ s/(\w+)(?:=\"([^\"]+)\")\s*//s) {
157 6 50       36 push @args,$1,defined $2 ? $2 : $1;
158             }
159 6         13 my $call = "directive_$name";
160 6         34 $self->$call(@args);
161 6         24 goto &text;
162             }
163 0         0 die "Directives are not yet supported at $self->{engine}->{template_root}/$self->{filename} line $..\n";
164             }
165              
166             sub runnow {
167 3     3 0 5 my ($self) = @_;
168 3         3 my $runnow;
169 3         15 my $beginline = $. -1;
170 3         15 my @out = eval $self->get_block;
171 3         11 my $error = $@;
172 3 50       10 if ($error) {
173 0         0 $error =~ s/at.*?line\s+(\d+).*$//s;
174 0         0 die "$error in compile-time code block at $self->{engine}->{template_root}/$self->{filename} line ".($beginline+$1)."\n";
175             }
176 3         3 push @{$self->{out}}, map { s#\\#\\\\#g; s#'#\\'#g; $_ } @out;
  3         9  
  3         7  
  3         6  
  3         7  
177 3         11 goto &text;
178             }
179              
180             sub static_include {
181 6     6 0 11 my ($self,$filename,$directory) = @_;
182 6         13 local *INPUT;
183 6 50       261 open INPUT,"< $self->{engine}->{template_root}/$filename" or die "Cannot open $self->{engine}->{template_root}/$filename at $self->{filename} line $..\n";
184 6         23 my $parser = $self->clone;
185 6         12 my @directory_option = ();
186 6 100       19 @directory_option = ( directory => $directory ) if defined $directory;
187 6         98 my ($dummy,$define,$out) = $parser->parse_template(input => \*INPUT, filename => $filename, classname => 'dummy', @directory_option);
188 6 100       20 push @{$self->{out}},"'" if $self->{in_quotes};
  2         6  
189 6 100       15 push @{$self->{out}},';' if $self->{pushing};
  2         5  
190 6         7 $self->{pushing} = 0;
191 6         10 $self->{in_quotes} = 0;
192 6         8 push @{$self->{out}},@$out;
  6         26  
193 6         9 push @{$self->{define}},@$define;
  6         12  
194 6         15 $self->set_line;
195             }
196              
197             sub directive_include {
198 3     3 0 9 my ($self,%args) = @_;
199 3 50       8 die "No file argument for include directive at $self->{engine}->{template_root}/$self->{filename} line $..\n" unless defined $args{file};
200             # warn "including $self->{directory}/$args{file}\n";
201 3         28 my $new_filename = $self->{engine}->normalize_path("$self->{directory}/$args{file}");
202             # warn "that's $new_filename now\n";
203 3         15 $self->static_include($new_filename);
204             }
205              
206             sub directive_find {
207 3     3 0 7 my ($self,%args) = @_;
208 3 50       7 die "No file argument for find directive at $self->{engine}->{template_root}/$self->{filename} line $..\n" unless defined $args{file};
209 3         15 my $path = $self->{engine}->normalize_path("/$self->{directory}");
210 3         6 my $filename = $args{file};
211 3         3 my $found = 0;
212 3         3 while (1) {
213 8 100       130 $found = 1, last if -f "$self->{engine}->{template_root}$path/$filename";
214 5 50       10 last if $path eq '';
215 5         21 $path =~ s#/[^/]*$##;
216 5         5 next;
217             }
218 3 50       8 die "File $args{file} not found at $self->{engine}->{template_root}/$self->{filename} line $..\n" unless $found;
219 3         17 $self->static_include("$path/$filename",$self->{directory});
220             }
221              
222             sub directive_path {
223 0     0 0   my ($self,%args) = @_;
224 0 0         die "No file argument for path directive at $self->{engine}->{template_root}/$self->{filename} line $..\n" unless defined $args{file};
225 0           my $path = $self->{engine}->normalize_path("/$self->{directory}");
226 0           my $filename = $args{file};
227 0           my $found = 0;
228 0           while (1) {
229 0 0         $found = 1, last if -f "$self->{engine}->{template_root}$path/$filename";
230 0 0         last if $path eq '';
231 0           $path =~ s#/[^/]*$##;
232 0           next;
233             }
234 0 0         die "File $args{file} not found at $self->{engine}->{template_root}/$self->{filename} line $..\n" unless $found;
235 0 0         push @{$self->{out}},"'" if $self->{in_quotes};
  0            
236 0           $self->{in_quotes} = 0;
237 0 0         push @{$self->{out}},'push @o' unless $self->{pushing};
  0            
238 0           $self->{pushing} = 1;
239 0           push @{$self->{out}},',',$path;
  0            
240             }
241              
242            
243              
244              
245             1;
246              
247             __END__