/usr/share/perl5/Text/MicroMason/Base.pm is in libtext-micromason-perl 2.16-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 | package Text::MicroMason::Base;
use strict;
require Carp;
######################################################################
######################################################################
use Class::MixinFactory -hasafactory;
for my $factory ( (__PACKAGE__)->mixin_factory ) {
$factory->base_class( "Text::MicroMason::Base" );
$factory->mixin_prefix( "Text::MicroMason" );
}
######################################################################
######################################################################
sub new {
my $callee = shift;
my ( @traits, @attribs );
while ( scalar @_ ) {
if ( $_[0] =~ /^\-(\w+)$/ ) {
push @traits, $1;
shift;
} else {
push @attribs, splice(@_, 0, 2);
}
}
if ( scalar @traits ) {
die("Adding moxins to an existing class not supported yet!")
unless ( $callee eq __PACKAGE__ );
$callee->class( @traits )->create( @attribs )
} else {
$callee->create( @attribs )
}
}
######################################################################
# $mason = $class->create( %options );
# $clone = $object->create( %options );
sub create {
my $referent = shift;
if ( ! ref $referent ) {
bless { $referent->defaults(), @_ }, $referent;
} else {
bless { $referent->defaults(), %$referent, @_ }, ref $referent;
}
}
sub defaults {
return ()
}
######################################################################
######################################################################
# $code_ref = $mason->compile( text => $template, %options );
# $code_ref = $mason->compile( file => $filename, %options );
# $code_ref = $mason->compile( handle => $filehandle, %options );
sub compile {
my ( $self, $src_type, $src_data, %options ) = @_;
($self, $src_type, $src_data) = $self->prepare($src_type, $src_data,%options);
my $code = $self->interpret( $src_type, $src_data );
$self->eval_sub( $code )
or $self->croak_msg( "MicroMason compilation failed: $@\n". _number_lines($code)."\n" );
}
# Internal helper to number the lines in the compiled template when compilation croaks
sub _number_lines {
my $code = shift;
my $n = 0;
return join("\n", map { sprintf("%4d %s", $n++, $_) } split(/\n/, $code)).
"\n** Please use Text::MicroMason->new\(-LineNumbers\) for better diagnostics!";
}
######################################################################
# $result = $mason->execute( code => $subref, @arguments );
# $result = $mason->execute( $src_type, $src_data, @arguments );
# $result = $mason->execute( $src_type, $src_data, \%options, @arguments );
sub execute {
my $self = shift;
my $sub = ( $_[0] eq 'code' ) ? do { shift; shift } :
$self->compile( shift, shift, ref($_[0]) ? %{ shift() } : () )
or $self->croak_msg("MicroMason compilation failed: $@");
&$sub( @_ );
}
######################################################################
######################################################################
# ($self, $src_type, $src_data) = $self->prepare($src_type, $src_data, %options)
sub prepare {
my ( $self, $src_type, $src_data, %options ) = @_;
$self = $self->create( %options ) if ( scalar keys %options );
return ( $self, $src_type, $src_data );
}
######################################################################
# $perl_code = $mason->interpret( $src_type, $src_data );
sub interpret {
my ( $self, $src_type, $src_data ) = @_;
my $template = $self->read( $src_type, $src_data );
my @tokens = $self->lex( $template );
my $code = $self->assemble( @tokens );
# Source file and line number
my $source_line = $self->source_file_line_label( $src_type, $src_data );
return $source_line . "\n" . $code;
}
# $line_number_comment = $mason->source_file_line_label( $src_type, $src_data );
sub source_file_line_label {
my ( $self, $src_type, $src_data ) = @_;
if ( $src_type eq 'file' ) {
return qq(# line 1 "$src_data");
}
my @caller;
my $call_level;
do { @caller = caller( ++ $call_level ) }
while ( $caller[0] =~ /^Text::MicroMason/ or $self->isa($caller[0]) );
my $package = ( $caller[1] || $0 );
qq{# line 1 "text template (compiled at $package line $caller[2])"}
}
######################################################################
# $code_ref = $mason->eval_sub( $perl_code );
sub eval_sub {
my $m = shift;
package Text::MicroMason::Commands;
eval( shift )
}
######################################################################
######################################################################
# $template = $mason->read( $src_type, $src_data );
sub read {
my ( $self, $src_type, $src_data ) = @_;
my $src_method = "read_$src_type";
$self->can($src_method)
or $self->croak_msg("Unsupported source type '$src_type'");
$self->$src_method( $src_data );
}
# $template = $mason->read_text( $template );
sub read_text {
ref($_[1]) ? $$_[1] : $_[1];
}
# $contents = $mason->read_file( $filename );
sub read_file {
my ( $self, $file ) = @_;
local *FILE;
open FILE, "$file" or $self->croak_msg("MicroMason can't open $file: $!");
local $/ = undef;
local $_ = <FILE>;
close FILE or $self->croak_msg("MicroMason can't close $file: $!");;
return $_;
}
# $contents = $mason->read_handle( $filehandle );
sub read_handle {
my ( $self, $handle ) = @_;
my $fh = (ref $handle eq 'GLOB') ? $handle : $$handle;
local $/ = undef;
<$fh>
}
######################################################################
# @token_pairs = $mason->lex( $template );
sub lex {
my $self = shift;
local $_ = "$_[0]";
my @tokens;
my $lexer = $self->can('lex_token')
or $self->croak_msg('Unable to lex_token(); must select a syntax mixin');
# warn "Lexing: " . pos($_) . " of " . length($_) . "\n";
until ( /\G\z/gc ) {
my @parsed = &$lexer( $self ) or
/\G ( .{0,20} ) /gcxs
&& die "MicroMason parsing halted at '$1'\n";
push @tokens, @parsed;
}
return @tokens;
}
# ( $type, $value ) = $mason->lex_token();
sub lex_token {
die "The lex_token() method is abstract and must be provided by a subclass";
}
######################################################################
######################################################################
# Text elements used for subroutine assembly
sub assembler_rules {
template => [ qw( $sub_start $init_errs $init_output
$init_args @perl $return_output $sub_end ) ],
# Subroutine scafolding
sub_start => 'sub { ',
sub_end => '}',
init_errs =>
'local $SIG{__DIE__} = sub { die "MicroMason execution failed: ", @_ };',
# Argument processing elements
init_args => 'my %ARGS = @_ if ($#_ % 2);',
# Output generation
init_output => sub { my $m = shift; my $sub = $m->{output_sub} ? '$m->{output_sub}' : 'sub {push @OUT, @_}'; 'my @OUT; my $_out = ' . $sub . ';' },
add_output => sub { my $m = shift; $m->{output_sub} ? '&$_out' : 'push @OUT,' },
return_output => 'join("", @OUT)',
# Mapping between token types
text_token => 'perl OUT( QUOTED );',
expr_token => "perl OUT( \"\".do{\nTOKEN\n} );",
# the "". here forces string context, and should hopefully make
# 'uninitialized' warnings appear closer to their source, rather
# than at the big join "", @OUT; at the end
file_token => "perl OUT( \$m->execute( file => do {\nTOKEN\n} ) );",
# Note that we need newline after TOKEN here in case it ends with a comment.
}
sub assembler_vars {
my $self = shift;
my %assembler = $self->assembler_rules();
my @assembly = @{ delete $assembler{ template } };
my %token_map = map { ( /^(.*?)_token$/ )[0] => delete $assembler{$_} }
grep { /_token$/ } keys %assembler;
my %fragments = map { $_ => map { ref($_) ? &{$_}( $self ) : $_ } $assembler{$_} } keys %assembler;
return( \@assembly, \%fragments, \%token_map );
}
# $perl_code = $mason->assemble( @tokens );
sub assemble {
my $self = shift;
my @tokens = @_;
my ( $order, $fragments, $token_map ) = $self->assembler_vars();
my %token_streams = map { $_ => [] } map { ( /^\W?\@(\w+)$/ ) } @$order;
while ( scalar @tokens ) {
my ( $type, $token ) = splice( @tokens, 0, 2 );
unless ( $token_streams{$type} or $token_map->{$type} ) {
my $method = "assemble_$type";
my $sub = $self->can( $method )
or $self->croak_msg( "Unexpected token type '$type': '$token'" );
($type, $token) = &$sub( $self, $token );
}
if ( my $typedef = $token_map->{ $type } ) {
# Perform token map substitution in a single pass so that uses of
# OUT in the token text are not improperly converted to output calls.
# -- Simon, 2009-11-14
my %substitution_map = (
'OUT' => $fragments->{add_output},
'TOKEN' => $token,
'QUOTED' => "qq(\Q$token\E)",
);
$typedef =~ s/\b(OUT|TOKEN|QUOTED)\b/$substitution_map{$1}/g;
( $type, $token ) = split ' ', $typedef, 2;
}
my $ary = $token_streams{$type}
or $self->croak_msg( "Unexpected token type '$type': '$token'" );
push @$ary, $token
}
join( "\n", map {
/^(\W+)(\w+)$/ or $self->croak_msg("Can't assemble $_");
if ( $1 eq '$' ) {
$fragments->{ $2 }
} elsif ( $1 eq '@' ) {
@{ $token_streams{ $2 } }
} elsif ( $1 eq '!@' ) {
reverse @{ $token_streams{ $2 } }
} elsif ( $1 eq '-@' ) {
()
} else {
$self->croak_msg("Can't assemble $_");
}
} @$order );
}
######################################################################
######################################################################
sub croak_msg {
local $Carp::CarpLevel = 2;
shift and Carp::croak( ( @_ == 1 ) ? $_[0] : join(' ', map _printable(), @_) )
}
my %Escape = (
( map { chr($_), unpack('H2', chr($_)) } (0..255) ),
"\\"=>'\\', "\r"=>'r', "\n"=>'n', "\t"=>'t', "\""=>'"'
);
# $special_characters_escaped = _printable( $source_string );
sub _printable {
local $_ = scalar(@_) ? (shift) : $_;
return "(undef)" unless defined;
s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$Escape{$1}/sgo;
/[^\w\d\-\:\.\']/ ? "q($_)" : $_;
}
######################################################################
sub cache_key {
my $self = shift;
my ($src_type, $src_data, %options) = @_;
return $src_data;
}
1;
__END__
######################################################################
=head1 NAME
Text::MicroMason::Base - Abstract Template Compiler
=head1 SYNOPSIS
Create a MicroMason object to interpret the templates:
use Text::MicroMason;
my $mason = Text::MicroMason->new();
Use the execute method to parse and evalute a template:
print $mason->execute( text=>$template, 'name'=>'Dave' );
Or compile it into a subroutine, and evaluate repeatedly:
$coderef = $mason->compile( text=>$template );
print $coderef->('name'=>'Dave');
print $coderef->('name'=>'Bob');
Templates stored in files can be run directly or included in others:
print $mason->execute( file=>"./greeting.msn", 'name'=>'Charles');
=head1 DESCRIPTION
Text::MicroMason::Base is an abstract superclass that provides a parser
and execution environment for an extensible templating system.
=head2 Public Methods
=over 4
=item new()
$mason = Text::MicroMason::Base->new( -Mixin1, -Mixin2, %attribs );
Creates a new Text::MicroMason object with mixins and attributes.
Arguments beginning with a dash will be added as mixin classes.
Other arguments are added to the hash of attributes.
=item compile()
$code_ref = $mason->compile( text => $template, %options );
$code_ref = $mason->compile( file => $filename, %options );
Parses the provided template and converts it into a new Perl subroutine.
=item execute()
$result = $mason->execute( text => $template, @arguments );
$result = $mason->execute( file => $filename, @arguments );
$result = $mason->execute( code => $code_ref, @arguments );
$result = $mason->execute( $type => $source, \%options, @arguments );
Returns the results produced by the template, given the provided arguments.
=back
=head2 Attributes
Attributes can be set in a call to new() and locally overridden in a call to compile().
=over 4
=item output_sub
Optional reference to a subroutine to call with each piece of template output. If this is enabled, template subroutines will return an empty string.
=back
=head2 Private Methods
The following internal methods are used to implement the public interface described above, and may be overridden by subclasses and mixins.
=over 4
=item class()
$class = Text::MicroMason::Base->class( @Mixins );
Creates a subclass of this package that also inherits from the other classes named. Provided by Class::MixinFactory::HasAFactory.
=item create()
$mason = $class->create( %options );
$clone = $mason->create( %options );
Creates a new instance with the provided key value pairs.
To obtain the functionality of one of the supported mixin classes, use the class method to generate the mixed class before calling create(), as is done by new().
=item defaults()
This class method is called by new() to provide key-value pairs to be included in the new instance.
=item prepare()
($self, $src_type, $src_data) = $self->prepare($src_type, $src_data, %options)
Called by compile(), the prepare method allows for single-use attributes and provides a hook for mixin functionality.
The prepare method provides a hook for mixins to normalize or resolve the template source type and value arguments in various ways before the template is read using one of the read_type() methods.
It returns an object reference that may be a clone of the original mason object with various compile-time attributes applied. The cloning is a shallow copy performed by the create() method. This means that the $m object visible to a template may not be the same as the MicroMason object on which compile() was originally called.
Please note that this clone-on-prepare behavior is subject to change in future releases.
=item interpret
$perl_code = $mason->interpret( $src_type, $src_data );
Called by compile(), the interpret method then calls the read(), lex(), and assemble() methods.
=item read
$template = $mason->read( $src_type, $src_data );
Called by interpret(). Calls one of the below read_* methods.
=item read_text
$template = $mason->read_text( $template );
Called by read() when the template source type is "text", this method simply returns the value of the text string passed to it.
=item read_file
( $contents, %path_info ) = $mason->read_file( $filename );
Called by read() when the template source type is "file", this method reads and returns the contents of the named file.
=item read_handle
$template = $mason->read_handle( $filehandle );
Called by read() when the template source type is "handle", this method reads and returns the contents of the filehandle passed to it.
=item lex
@token_pairs = $mason->lex( $template );
Called by interpret(). Parses the source text and returns a list of pairs of token types and values. Loops through repeated calls to lex_token().
=item lex_token
( $type, $value ) = $mason->lex_token();
Attempts to parse a token from the template text stored in the global $_ and returns a token type and value. Returns an empty list if unable to parse further due to an error.
Abstract method; must be implemented by subclasses.
=item assemble
$perl_code = $mason->assemble( @tokens );
Called by interpret(). Assembles the parsed token series into the source code for the equivalent Perl subroutine.
=item assembler_rules()
Returns a hash of text elements used for Perl subroutine assembly. Used by assemble().
The assembly template defines the types of blocks supported and the order they appear in, as well as where other standard elements should go. Those other elements also appear in the assembler hash.
=item eval_sub
$code_ref = $mason->eval_sub( $perl_code );
Called by compile(). Compiles the Perl source code for a template using eval(), and returns a code reference.
=item croak_msg
Called when a fatal exception has occurred.
=item NEXT
Enhanced superclass method dispatch for use inside mixin class methods. Allows mixin classes to redispatch to other classes in the inheritance tree without themselves inheriting from anything. Provided by Class::MixinFactory::NEXT.
=back
=head2 Private Functions
=over 4
=item _printable
$special_characters_escaped = _printable( $source_string );
Converts non-printable characters to readable form using the standard backslash notation, such as "\n" for newline.
=back
=head1 EXTENDING
You can add functionality to this module by creating subclasses or mixin classes.
To create a subclass, just inherit from the base class or some dynamically-assembled class. To create your own mixin classes which can be combined with other mixin features, examine the operation of the class() and NEXT() methods.
Key areas for subclass writers are:
=over 4
=item prepare
You can intercept and re-write template source arguments by overriding this method.
=item read_*
You can support a new template source type by creating a method with a corresponding name prefixed by "read_". It is passed the template source value and should return the raw text to be lexed.
For example, if a subclass defined a method named read_from_db, callers could compile templates by calling C<-E<gt>compile( from_db =E<gt> 'welcome-page' )>.
=item lex_token
Replace this to parse a new template syntax. Is receives the text to be parsed in $_ and should match from the current position to return the next token type and its contents.
=item assembler_rules
The assembler data structure is used to construct the Perl subroutine for a parsed template.
=item assemble_*
You can support a new token type be creating a method with a corresponding name prefixed by "assemble_". It is passed the token value or contents, and should return a new token pair that is supported by the assembler template.
For example, if a subclass defined a method named assemble_sqlquery, callers could compile templates that contained a C<E<lt>%sqlqueryE<gt> ... E<lt>/%sqlqueryE<gt>> block. The assemble_sqlquery method could return a C<< perl => $statements >> pair with Perl code that performed some appropriate action.
=item compile
You can wrap or cache the results of this method, which is the primary public interface.
=item execute
You typically should not depend on overriding this method because callers can invoke the compiled subroutines directly without calling execute.
=back
=head1 SEE ALSO
For an overview of this templating framework, see L<Text::MicroMason>.
For distribution, installation, support, copyright and license
information, see L<Text::MicroMason::Docs::ReadMe>.
=cut
|