📄 templateparser.pm
字号:
package TemplateParser;
# ************************************************************
# Description : Parses the template and fills in missing values
# Author : Chad Elliott
# Create Date : 5/17/2002
# ************************************************************
# ************************************************************
# Pragmas
# ************************************************************
use strict;
use Parser;
use vars qw(@ISA);
@ISA = qw(Parser);
# ************************************************************
# Data Section
# ************************************************************
my(%keywords) = ('if' => 1,
'else' => 1,
'endif' => 1,
'noextension' => 1,
'dirname' => 1,
'basename' => 1,
'basenoextension' => 1,
'foreach' => 1,
'forfirst' => 1,
'fornotfirst' => 1,
'fornotlast' => 1,
'forlast' => 1,
'endfor' => 1,
'comment' => 1,
'flag_overrides' => 1,
'marker' => 1,
'uc' => 1,
'lc' => 1,
);
# ************************************************************
# Subroutine Section
# ************************************************************
sub new {
my($class) = shift;
my($prjc) = shift;
my($self) = Parser::new($class);
$self->{'prjc'} = $prjc;
$self->{'ti'} = $prjc->get_template_input();
$self->{'cslashes'} = $prjc->convert_slashes();
$self->{'addtemp'} = $prjc->get_addtemp();
$self->{'crlf'} = $prjc->crlf();
$self->{'clen'} = length($self->{'crlf'});
$self->{'values'} = {};
$self->{'defaults'} = {};
$self->{'lines'} = [];
$self->{'built'} = '';
$self->{'sstack'} = [];
$self->{'lstack'} = [];
$self->{'if_skip'} = 0;
$self->{'foreach'} = {};
$self->{'foreach'}->{'count'} = -1;
$self->{'foreach'}->{'nested'} = 0;
$self->{'foreach'}->{'name'} = [];
$self->{'foreach'}->{'names'} = [];
$self->{'foreach'}->{'text'} = [];
$self->{'foreach'}->{'scope'} = [];
$self->{'foreach'}->{'temp_scope'} = [];
$self->{'foreach'}->{'processing'} = 0;
return $self;
}
sub basename {
my($self) = shift;
my($file) = shift;
for(my $i = length($file) - 1; $i >= 0; --$i) {
my($ch) = substr($file, $i, 1);
if ($ch eq '/' || $ch eq '\\') {
## The template file may use this value (<%basename_found%>)
## to determine whether a basename removed the directory or not
$self->{'values'}->{'basename_found'} = 1;
return substr($file, $i + 1);
}
}
delete $self->{'values'}->{'basename_found'};
return $file;
}
sub dirname {
my($self) = shift;
my($file) = shift;
for(my $i = length($file) - 1; $i != 0; --$i) {
my($ch) = substr($file, $i, 1);
if ($ch eq '/' || $ch eq '\\') {
## The template file may use this value (<%dirname_found%>)
## to determine whether a dirname removed the basename or not
$self->{'values'}->{'dirname_found'} = 1;
return substr($file, 0, $i);
}
}
delete $self->{'values'}->{'dirname_found'};
return '.';
}
sub strip_line {
my($self) = shift;
my($line) = shift;
## Override strip_line() from Parser.
## We need to preserve leading space and
## there is no comment string in templates.
++$self->{'line_number'};
$line =~ s/\s+$//;
return $line;
}
## Append the current value to the line that is being
## built. This line may be a foreach line or a general
## line without a foreach.
sub append_current {
my($self) = shift;
my($value) = shift;
my($index) = $self->{'foreach'}->{'count'};
if ($index >= 0) {
$self->{'foreach'}->{'text'}->[$index] .= $value;
}
else {
$self->{'built'} .= $value;
}
}
sub adjust_value {
my($self) = shift;
my($name) = shift;
my($value) = shift;
## Perform any additions, subtractions
## or overrides for the template values.
my($addtemp) = $self->{'addtemp'};
foreach my $at (keys %$addtemp) {
if ($at eq $name) {
my($val) = $$addtemp{$at};
if ($$val[0] > 0) {
if (UNIVERSAL::isa($value, 'ARRAY')) {
$value = [ $$val[1], @$value ];
}
else {
$value = "$$val[1] $value";
}
}
elsif ($$val[0] < 0) {
my($parts) = undef;
if (UNIVERSAL::isa($value, 'ARRAY')) {
my(@copy) = @$value;
$parts = \@copy;
}
else {
$parts = $self->create_array($value);
}
$value = '';
foreach my $part (@$parts) {
if ($part ne $$val[1] && $part ne '') {
$value .= "$part ";
}
}
$value =~ s/^\s+//;
$value =~ s/\s+$//;
}
else {
$value = $$val[1];
}
}
}
return $value;
}
sub set_current_values {
my($self) = shift;
my($name) = shift;
my($set) = 0;
## If any value within a foreach matches the name
## of a hash table within the template input we will
## set the values of that hash table in the current scope
if (defined $self->{'ti'}) {
my($counter) = $self->{'foreach'}->{'count'};
if ($counter >= 0) {
my($value) = $self->{'ti'}->get_value($name);
if (defined $value && UNIVERSAL::isa($value, 'HASH')) {
my(%copy) = ();
foreach my $key (keys %$value) {
$copy{$key} = $self->adjust_value($key, $$value{$key});
}
$self->{'foreach'}->{'temp_scope'}->[$counter] = \%copy;
$set = 1;
}
}
}
return $set;
}
sub get_nested_value {
my($self) = shift;
my($name) = shift;
my($value) = undef;
if ($name =~ /^(.*)\->(\w+)/) {
my($pre) = $1;
my($post) = $2;
my($base) = $self->get_value($pre);
if (defined $base) {
$value = $self->{'prjc'}->get_special_value($pre, $post, $base);
}
}
return $value;
}
sub get_value {
my($self) = shift;
my($name) = shift;
my($value) = undef;
my($counter) = $self->{'foreach'}->{'count'};
## First, check the temporary scope (set inside a foreach)
if ($counter >= 0) {
while(!defined $value && $counter >= 0) {
$value = $self->{'foreach'}->{'temp_scope'}->[$counter]->{$name};
--$counter;
}
$counter = $self->{'foreach'}->{'count'};
}
if (!defined $value) {
## Next, check for a template value
if (defined $self->{'ti'}) {
$value = $self->{'ti'}->get_value($name);
if (defined $value) {
$value = $self->adjust_value($name, $value);
}
}
if (!defined $value) {
## Next, check the inner to outer foreach
## scopes for overriding values
while(!defined $value && $counter >= 0) {
$value = $self->{'foreach'}->{'scope'}->[$counter]->{$name};
--$counter;
}
## Then get the value from the project creator
if (!defined $value) {
$value = $self->{'prjc'}->get_assignment($name);
## Then get it from our known values
if (!defined $value) {
$value = $self->{'values'}->{$name};
if (!defined $value) {
## Call back onto the project creator to allow
## it to fill in the value before defaulting to undef.
$value = $self->{'prjc'}->fill_value($name);
if (!defined $value && $name =~ /\->/) {
$value = $self->get_nested_value($name);
}
}
}
}
}
}
return $self->{'prjc'}->relative($value);
}
sub get_value_with_default {
my($self) = shift;
my($name) = shift;
my($value) = $self->get_value($name);
if (defined $value) {
if (UNIVERSAL::isa($value, 'ARRAY')) {
$value = "@$value";
}
}
else {
$value = $self->{'defaults'}->{$name};
if (!defined $value) {
# print "DEBUG: WARNING: $name defaulting to empty string\n";
$value = '';
}
else {
# print "DEBUG: WARNING: $name using default value of $value\n";
$value = $self->adjust_value($name, $value);
}
$value = $self->{'prjc'}->relative($value);
}
return $value;
}
sub process_foreach {
my($self) = shift;
my($index) = $self->{'foreach'}->{'count'};
my($text) = $self->{'foreach'}->{'text'}->[$index];
my($status) = 1;
my($errorString) = '';
my(@values) = ();
my($names) = $self->create_array($self->{'foreach'}->{'names'}->[$index]);
my($name) = $self->{'foreach'}->{'name'}->[$index];
foreach my $n (@$names) {
my($vals) = $self->get_value($n);
if (defined $vals && $vals ne '') {
if (!UNIVERSAL::isa($vals, 'ARRAY')) {
$vals = $self->create_array($vals);
}
push(@values, @$vals);
}
if (!defined $name) {
$name = $n;
$name =~ s/s$//;
}
}
## Reset the text (it will be regenerated by calling parse_line
$self->{'foreach'}->{'text'}->[$index] = '';
if (defined $values[0]) {
my($scope) = $self->{'foreach'}->{'scope'}->[$index];
$$scope{'forlast'} = 0;
$$scope{'fornotlast'} = 1;
$$scope{'forfirst'} = 1;
$$scope{'fornotfirst'} = 0;
## If the foreach values are mixed (HASH and SCALAR), then
## remove the SCALAR values.
my($pset) = undef;
for(my $i = 0; $i <= $#values; ++$i) {
my($set) = $self->set_current_values($values[$i]);
if (!defined $pset) {
$pset |= $set;
}
else {
if ($pset && !$set) {
splice(@values, $i, 1);
$i = 0;
$pset = undef;
}
}
}
for(my $i = 0; $i <= $#values; ++$i) {
my($value) = $values[$i];
## Set the corresponding values in the temporary scope
$self->set_current_values($value);
## Set the special values that only exist
## within a foreach
if ($i != 0) {
$$scope{'forfirst'} = 0;
$$scope{'fornotfirst'} = 1;
}
if ($i == $#values) {
$$scope{'forlast'} = 1;
$$scope{'fornotlast'} = 0;
}
$$scope{'forcount'} = $i + 1;
## We don't use adjust_value here because these names
## are generated from a foreach and should not be adjusted.
$$scope{$name} = $value;
## A tiny hack for VC7
if ($name eq 'configuration') {
$self->{'prjc'}->update_project_info($self, 1,
['configuration', 'platform'],
'|');
}
## Now parse the line of text, each time
## with different values
++$self->{'foreach'}->{'processing'};
($status, $errorString) = $self->parse_line(undef, $text);
--$self->{'foreach'}->{'processing'};
if (!$status) {
last;
}
}
}
return $status, $errorString;
}
sub handle_end {
my($self) = shift;
my($name) = shift;
my($status) = 1;
my($errorString) = '';
my($end) = pop(@{$self->{'sstack'}});
pop(@{$self->{'lstack'}});
if (!defined $end) {
$status = 0;
$errorString = "ERROR: Unmatched $name\n";
}
elsif ($end eq 'endif') {
$self->{'if_skip'} = 0;
}
elsif ($end eq 'endfor') {
my($index) = $self->{'foreach'}->{'count'};
($status, $errorString) = $self->process_foreach();
if ($status) {
--$self->{'foreach'}->{'count'};
$self->append_current($self->{'foreach'}->{'text'}->[$index]);
}
}
return $status, $errorString;
}
sub get_flag_overrides {
my($self) = shift;
my($name) = shift;
my($type) = shift;
my($value) = undef;
my($file) = $self->get_value($name);
my($prjc) = $self->{'prjc'};
my($fo) = $prjc->{'flag_overrides'};
if (defined $file) {
## Replace the custom_type key with the actual custom type
if ($name =~ /^custom_type\->/) {
my($ct) = $self->get_value('custom_type');
if (defined $ct) {
$name = $ct;
}
}
foreach my $key (keys %$fo) {
if ($key =~ /^$name/) {
foreach my $of (keys %{$$fo{$key}}) {
my($cv) = $of;
if ($self->{'cslashes'}) {
$cv = $prjc->slash_to_backslash($of);
}
if ($cv eq $file) {
foreach my $ma (keys %{$prjc->{'matching_assignments'}}) {
if ($ma eq $key) {
foreach my $aname (@{$prjc->{'matching_assignments'}->{$ma}}) {
if ($aname eq $type &&
defined $$fo{$key}->{$of}->{$aname}) {
$value = $$fo{$key}->{$of}->{$aname};
last;
}
}
last;
}
}
last;
}
}
last;
}
}
}
return $prjc->relative($value);
}
sub handle_if {
my($self) = shift;
my($val) = shift;
my($name) = 'endif';
push(@{$self->{'lstack'}}, $self->get_line_number() . " $val");
if (!$self->{'if_skip'}) {
my($true) = 1;
push(@{$self->{'sstack'}}, $name);
if ($val !~ /\|\|/ && $val =~ /^!(.*)/) {
$val = $1;
$val =~ s/^\s+//;
$true = 0;
}
if ($val =~ /flag_overrides\(([^\)]+),\s*([^\)]+)\)/) {
$val = $self->get_flag_overrides($1, $2);
}
else {
if ($val =~ /\|\|/) {
my($str) = $val;
$val = undef;
foreach my $v (split(/\s*\|\|\s*/, $str)) {
my($p) = 0;
if ($v =~ /^!(.*)/) {
$p = $self->get_value($v);
if (defined $p) {
$p = undef;
}
else {
$p = 'some value';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -