微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

perl高级用法--通过学习Verilog::Getopt掌握package的写法

# See copyright,etc in below POD section.
######################################################################

package Verilog::Getopt;              # package name, 其中Verilog是目录
require 5.000;                        # 需要perl5.0以上
require Exporter;                     # Exporter 使得能够导出各个函数

use strict;
use vars qw($VERSION $Debug %skip_Basenames);      # 等同于our声明,导出变量
use Carp;                                          # package中使用warning或者die类似的功能。
use IO::File;
use File::Basename;
use File::Spec;
use Cwd;

######################################################################
#### Configuration Section

$VERSION = '3.305';

# Basenames we should ignore when recursing directories,# Because they contain large files of no relevance
foreach ( '.','..','CVS','.svn','.snapshot','blib',) {
    $Skip_Basenames{$_} = 1;
}

#######################################################################
#######################################################################
#######################################################################
# 类成员(可以有初值),本质是hash表。成员函数不再其中,凡是package声明的sub函数都是成员函数。
# 一般我们推荐将internal的成员(private) 声明成 _member 的形式。这些类成员和后面声明的函数可以同名,
# 但是他们是完全不同的东西。一般同名的函数会把返回的值放入这些同名的的成员中(如匿名数组或hash表)
# 在perl中哪些变量会声明成类成员呢? 一般用于configuration变量以及在各个函数中传递(即多个函数使用的
# 变量)。 @_ 指明从new参数中给出的值。这样可以给成员赋新值,或者加入新的成员。本例中就是options
# 如 $Opt = new Verilog::Getopt(gcc_style=>0)



sub new {
    @_ >= 1 or croak 'usage: Verilog::Getopt->new ({options})';
    my $class = shift;		# Class (Getopt Element)   类名
    $class ||= "Verilog::Getopt";  #   进一步确定类名

    my $self = {defines => {},incdir => ['.',],module_dir => ['.',libext => ['.v',library => [ ],gcc_style => 1,vcs_style => 1,fileline => 'Command_Line',unparsed => [],define_warnings => 1,depend_files => {},@_
		};
    bless $self,$class;         #洗礼成为类
    return $self;                #返回类指针
}

#######################################################################
# Option parsing

sub _filedir {
    my $self = shift;
    my $path = shift;
    $path =~ s![/\\][^/\\]*$!!   # ~~== my @dirs = File::Spec->splitdir( $path );
	or $path = ".";
    return "." if $path eq '';
    return $path
}

sub parameter_file {
    my $self = shift;
    my $filename = shift;
    my $relative = shift;

    print "*parameter_file $filename\n" if $Debug;
    my $optdir = ".";
    if ($relative) { $optdir = $self->_filedir($filename); }

    my $fh = IO::File->new("<$filename") or die "%Error: ".$self->fileline().": $! $filename\n";
    my $hold_fileline = $self->fileline();
    while (my $line = $fh->getline()) {
	chomp $line;
	$line =~ s/\/\/.*$//;
	next if $line =~ /^\s*$/;
	$self->fileline ("$filename:$.");
	my @p = (split /\s+/,"$line ");
	$self->_parameter_parse($optdir,@p);
    }
    $fh->close();
    $self->fileline($hold_fileline);
}

sub parameter {
    my $self = shift;
    # Parse VCS like parameters,and perform standard setup based on it
    # Return list of leftover parameters
    @{$self->{unparsed}} = ();
    $self->_parameter_parse('.',@_);
    return @{$self->{unparsed}};
}

sub _parameter_parse {
    my $self = shift;
    my $optdir = shift;
    # Internal: Parse list of VCS like parameters,and perform standard setup based on it
    foreach my $param (@_) {
	next if ($param =~ /^\s*$/);
	print " parameter($param)\n" if $Debug;

	### GCC & VCS style
	if ($param eq '-F'
	    || $param eq '-f') {
	    $self->{_parameter_next} = $param;
	}

	### VCS style
	elsif (($param eq '-v'
		|| $param eq '-y') && $self->{vcs_style}) {
	    $self->{_parameter_next} = $param;
	}
	elsif ($param =~ /^\+libext\+(.*)$/ && $self->{vcs_style}) {
	    my $ext = $1;
	    foreach (split /\+/,$ext) {
		$self->libext($_);
	    }
	}
	elsif ($param =~ /^\+incdir\+(.*)$/ && $self->{vcs_style}) {
	    $self->incdir($self->_parse_file_arg($optdir,$1));
	}
	elsif (($param =~ /^\+define\+([^+=]*)[+=](.*)$/
		|| $param =~ /^\+define\+(.*?)()$/) && $self->{vcs_style}) {
	    $self->define($1,$2,undef,1);
	}
	# Ignored
	elsif ($param =~ /^\+librescan$/ && $self->{vcs_style}) {
	}

	### GCC style
	elsif (($param =~ /^-D([^=]*)=(.*)$/
		|| $param =~ /^-D([^=]*)()$/) && $self->{gcc_style}) {
	    $self->define($1,1);
	}
	elsif (($param =~ /^-U([^=]*)$/) && $self->{gcc_style}) {
	    $self->undef($1);
	}
	elsif ($param =~ /^-I(.*)$/ && $self->{gcc_style}) {
	    $self->incdir($self->_parse_file_arg($optdir,$1));
	}

	# Second parameters
	elsif ($self->{_parameter_next}) {
	    my $pn = $self->{_parameter_next};
	    $self->{_parameter_next} = undef;
	    if ($pn eq '-F') {
		$self->parameter_file ($self->_parse_file_arg($optdir,$param),1);
	    }
	    elsif ($pn eq '-f') {
		$self->parameter_file ($self->_parse_file_arg($optdir,0);
	    }
	    elsif ($pn eq '-v') {
		$self->library ($self->_parse_file_arg($optdir,$param));
	    }
	    elsif ($pn eq '-y') {
		$self->module_dir ($self->_parse_file_arg($optdir,$param));
	    }
	    else {
		die "%Error: ".$self->fileline().": Bad internal next param ".$pn;
	    }
	}

	else { # UnkNown
	    push @{$self->{unparsed}},"$param"; # Must quote to convert Getopt to string,bug298
	}
    }
}

sub _parse_file_arg {
    my $self = shift;
    my $optdir = shift;
    my $relfilename = shift;
    # Parse filename on option line,expanding relative paths in -F's
    my $filename = $self->file_substitute($relfilename);
    if ($optdir ne "." && ! File::Spec->file_name_is_absolute($filename)) {
	$filename = File::Spec->catfile($optdir,$filename);
    }
    return $filename;
}

#######################################################################
# Accessors

sub fileline {
    my $self = shift;
    if (@_) { $self->{fileline} = shift; }
    return ($self->{fileline});
}
sub incdir {
    my $self = shift;
    if (@_) {
	my $token = shift;
	print "incdir $token\n" if $Debug;
	if (ref($token) && ref($token) eq 'ARRAY') {
	    @{$self->{incdir}} = @{$token};
	} else {
	    push @{$self->{incdir}},$self->file_abs($token);
	}
	$self->file_path_cache_flush();
    }
    return (wantarray ? @{$self->{incdir}} : $self->{incdir});
}
sub libext {
    my $self = shift;
    if (@_) {
	my $token = shift;
	print "libext $token\n" if $Debug;
	if (ref($token) && ref($token) eq 'ARRAY') {
	    @{$self->{libext}} = @{$token};
	} else {
	    push @{$self->{libext}},$token;
	}
	$self->file_path_cache_flush();
    }
    return (wantarray ? @{$self->{libext}} : $self->{libext});
}
sub library {
    my $self = shift;
    if (@_) {
	my $token = shift;
	print "library $token\n" if $Debug;
	if (ref($token) && ref($token) eq 'ARRAY') {
	    @{$self->{library}} = @{$token};
	} else {
	    push @{$self->{library}},$self->file_abs($token);
	}
    }
    return (wantarray ? @{$self->{library}} : $self->{library});
}
sub module_dir {
    my $self = shift;
    if (@_) {
	my $token = shift;
	print "module_dir $token\n" if $Debug;
	if (ref($token) && ref($token) eq 'ARRAY') {
	    @{$self->{module_dir}} = @{$token};
	} else {
	    push @{$self->{module_dir}},$self->file_abs($token);
	}
	$self->file_path_cache_flush();
    }
    return (wantarray ? @{$self->{module_dir}} : $self->{module_dir});
}
sub depend_files {
    my $self = shift;
    if (@_) {
	#@_ may be Getopt::Long::Parameters which aren't arrays,will stringify
	if (ref($_[0]) && ref($_[0]) eq 'ARRAY') {
	    $self->{depend_files} = {};
	    foreach my $fn (@{$_[0]}) {
		$self->{depend_files}{$fn} = 1;
	    }
	} else {
	    foreach my $fn (@_) {
		print "depend_files $fn\n" if $Debug;
		$self->{depend_files}{$fn} = 1;
	    }
	}
    }
    my @list = (sort (keys %{$self->{depend_files}}));
    return (wantarray ? @list : \@list);
}

sub get_parameters {
    my $self = shift;
    my %args = (gcc_stlyle => $self->{gcc_style},);
    # Defines
    my @params = ();
    foreach my $def ($self->define_names_sorted) {
	my $defvalue = $self->defvalue($def);
	$defvalue = "=".($defvalue||"") if (defined $defvalue && $defvalue ne "");
	if ($args{gcc_style}) {
	    push @params,"-D${def}${defvalue}";
	} else {
	    push @params,"+define+${def}${defvalue}";
	}
    }
    # Put all libexts on one line,else NC-Verilog will bitch
    my $exts="";
    foreach my $ext ($self->libext()) {
	$exts = "+libext" if !$exts;
	$exts .= "+$ext";
    }
    push @params,$exts if $exts;
    # Includes...
    foreach my $dir ($self->incdir()) {
	if ($args{gcc_style}) {
	    push @params,"-I${dir}";
	} else {
	    push @params,"+incdir+${dir}";
	}
    }
    foreach my $dir ($self->module_dir()) {
	push @params,"-y",$dir;
    }
    foreach my $dir ($self->library()) {
	push @params,"-v",$dir;
    }
    return (@params);
}

sub write_parameters_file {
    my $self = shift;
    my $filename = shift;
    # Write get_parameters to a file
    my $fh = IO::File->new(">$filename") or croak "%Error: $! writing $filename,";
    my @opts = $self->get_parameters();
    print $fh join("\n",@opts);
    $fh->close;
}

#######################################################################
# Utility functions

sub remove_duplicates {
    my $self = ref $_[0] && shift;
    # return list in same order,with any duplicates removed
    my @rtn;
    my %hit;
    foreach (@_) { push @rtn,$_ unless $hit{$_}++; }
    return @rtn;
}

sub file_skip_special {
    my $self = shift;
    my $filename = shift;
    $filename =~ s!.*[/\\]!!;
    return $Skip_Basenames{$filename};
}

sub file_abs {
    my $self = shift;
    my $filename = shift;
    # return absolute filename
    # If the user doesn't want this absolutification,they can just
    # make their own derived class and override this function.
    #
    # We don't absolutify files that don't have any path,# as file_path() will probably be used to resolve them.
    return $filename;
    return $filename if ("" eq dirname($filename));
    return $filename if File::Spec->file_name_is_absolute($filename);
    # Cwd::abspath() requires files to exist.  Too annoying...
    $filename = File::Spec->canonpath(File::Spec->catdir(Cwd::getcwd(),$filename));
    return $filename;
}

sub file_substitute {
    my $self = shift;
    my $filename = shift;
    my $out = $filename;
    while ($filename =~ /\$([A-Za-z_0-9]+)\b/g) {
	my $var = $1;
	if (defined $ENV{$var}) {
	    $out =~ s/\$var\b/$ENV{$var}/g;
	}
    }
    $out =~ s!^~!$ENV{HOME}/!;
    return $out;
}

sub file_path_cache_flush {
    my $self = shift;
    # Clear out a file_path cache,needed if the incdir/module_dirs change
    $self->{_file_path_cache} = {};
}

sub file_path {
    my $self = shift;
    my $filename = shift;
    my $lookup_type = shift || 'all';
    # return path to given filename using library directories & files,or undef
    # locations are cached,because -r can be a very slow operation

    defined $filename or carp "%Error: Undefined filename,";
    return $self->{_file_path_cache}{$filename} if defined $self->{_file_path_cache}{$filename};
    if (-r $filename && !-d $filename) {
	$self->{_file_path_cache}{$filename} = $filename;
	$self->depend_files($filename);
	return $filename;
    }
    # Try expanding environment
    $filename = $self->file_substitute($filename);
    if (-r $filename && !-d $filename) {
	$self->{_file_path_cache}{$filename} = $filename;
	$self->depend_files($filename);
	return $filename;
    }

    # What paths to use?
    my @dirlist;
    if ($lookup_type eq 'module') {
	@dirlist = $self->module_dir();
    } elsif ($lookup_type eq 'include') {
	@dirlist = $self->incdir();
    } else {  # all
	# Might be more obvIoUs if -y had priority,but we'll remain back compatible
	@dirlist = ($self->incdir(),$self->module_dir());
    }
    # Expand any envvars in incdir/moduledir
    @dirlist = map {$self->file_substitute($_)} @dirlist;

    # Check each search path
    # We use both the incdir and moduledir.  This isn't strictly correct,# but it's fairly silly to have to specify both all of the time.
    my %checked_dir = ();
    my %checked_file = ();
    foreach my $dir (@dirlist) {
	next if $checked_dir{$dir}; $checked_dir{$dir}=1;  # -r can be quite slow
	# Check each postfix added to the file
	foreach my $postfix ("",@{$self->{libext}}) {
	    my $found = "$dir/$filename$postfix";
	    next if $checked_file{$found}; $checked_file{$found}=1;  # -r can be quite slow
	    if (-r $found && !-d $found) {
		$self->{_file_path_cache}{$filename} = $found;
		$self->depend_files($found);
		return $found;
	    }
	}
    }

    return $filename;	# Let whoever needs it discover it doesn't exist
}

sub libext_matches {
    my $self = shift;
    my $filename = shift;
    return undef if !$filename;
    foreach my $postfix (@{$self->{libext}}) {
	my $re = quoteMeta($postfix) . "\{1}quot;;
	return $filename if ($filename =~ /$re/);
    }
    return undef;
}

sub map_directories {
    my $self = shift;
    my $func = shift;
    # Execute map function on all directories listed in self.
    {
	my @newdir = $self->incdir();
	@newdir = map {&{$func}} @newdir;
	$self->incdir(\@newdir);
    }
    {
	my @newdir = $self->module_dir();
	@newdir = map {&{$func}} @newdir;
	$self->module_dir(\@newdir);
    }
}

#######################################################################
# Getopt functions

sub define_names_sorted {
    my $self = shift;
    return (sort (keys %{$self->{defines}}));
}

sub defcmdline {
    my $self = shift;
    my $token = shift;
    my $val = $self->{defines}{$token};
    if (ref $val) {
	return $val->[2];
    } else {
	return undef;
    }
}

sub defparams {
    my $self = shift;
    my $token = shift;
    my $val = $self->{defines}{$token};
    if (!defined $val) {
	return undef;
    } elsif (ref $val && defined $val->[1]) {
	return $val->[1];  # Has parameters hash,return param list or undef
    } else {
	return 0;
    }
}
sub defvalue {
    my $self = shift;
    my $token = shift;
    my $val = $self->{defines}{$token};
    (defined $val) or carp "%Warning: ".$self->fileline().": No deFinition for $token,";
    if (ref $val) {
	return $val->[0];  # Has parameters,return just value
    } else {
	return $val;
    }
}
sub defvalue_Nowarn {
    my $self = shift;
    my $token = shift;
    my $val = $self->{defines}{$token};
    if (ref $val) {
	return $val->[0];  # Has parameters,return just value
    } else {
	return $val;
    }
}
sub define {
    my $self = shift;
    if (@_) {
	my $token = shift;
	my $value = shift;
	my $params = shift;
	my $cmdline = shift;
	print "Define $token ".($params||'')."= $value\n" if $Debug;
	my $oldval = $self->{defines}{$token};
	my $oldparams;
	if (ref $oldval eq 'ARRAY') {
	    ($oldval,$oldparams) = @{$oldval};
	}
	if (defined $oldval
	    && (($oldval ne $value)
		|| (($oldparams||'') ne ($params||'')))
	    && $self->{define_warnings}) {
	    warn "%Warning: ".$self->fileline().": Redefining `$token\n";
	}
	if ($params || $cmdline) {
	    $self->{defines}{$token} = [$value,$params,$cmdline];
	} else {
	    $self->{defines}{$token} = $value;
	}
    }
}
sub undef {
    my $self = shift;
    my $token = shift;
    my $oldval = $self->{defines}{$token};
    # We no longer warn about undefing something that doesn't exist,as other compilers don't
    #(defined $oldval or !$self->{define_warnings})
    #	or carp "%Warning: ".$self->fileline().": No deFinition to undef for $token,";
    delete $self->{defines}{$token};
}

sub undefineall {
    my $self = shift;
    foreach my $def (keys %{$self->{defines}}) {
	if (!$self->defcmdline($def)) {
	    delete $self->{defines}{$def};
	}
    }
}

sub remove_defines {
    my $self = shift;
    my $sym = shift;
    my $val = "x";
    while (defined $val) {
	last if $sym eq $val;
	(my $xsym = $sym) =~ s/^\`//;
	$val = $self->defvalue_Nowarn($xsym);  #Undef if not found
	$sym = $val if defined $val;
    }
    return $sym;
}

######################################################################
### Package return
1;
__END__

=pod

=head1 NAME

Verilog::Getopt - Get Verilog command line options

=head1 SYnopSIS

  use Verilog::Getopt;

  my $opt = new Verilog::Getopt;
  $opt->parameter (qw( +incdir+standard_include_directory ));

  @ARGV = $opt->parameter (@ARGV);
  ...
  print "Path to foo.v is ",$opt->file_path('foo.v');

=head1 DESCRIPTION

Verilog::Getopt provides standardized handling of options similar to
Verilog/VCS and cc/GCC.

=over 4

=item $opt = Verilog::Getopt->new ( I<opts> )

Create a new Getopt.  If gcc_style=>0 is passed as a parameter,parsing of
GCC-like parameters is disabled.  If vcs_style=>0 is passed as a parameter,parsing of VCS-like parameters is disabled.

=item $self->file_path ( I<filename>,[I<lookup_type>] )

Returns a new path to the filename,using the library directories and
search paths to resolve the file.  Optional lookup_type is 'module','include',or 'all',to use only module_dirs,incdirs,or both for the
lookup.

=item $self->get_parameters ( )

Returns a list of parameters that when passed through $self->parameter()
should result in the same state.  Often this is used to form command lines
for downstream programs that also use Verilog::Getopt.

=item $self->parameter ( \@params )

Parses any recognized parameters in the referenced array,removing the
standard parameters and returning a array with all unparsed parameters.

The below list shows the VCS-like parameters that are supported,and the
functions that are called:

    +libext+I<ext>+I<ext>...	libext (I<ext>)
    +incdir+I<dir>		incdir (I<dir>)
    +define+I<var>[+=]I<value>	define (I<var>,I<value>)
    +define+I<var>		define (I<var>,undef)
    +librescan		Ignored
    -F I<file>		Parse parameters in file relatively
    -f I<file>		Parse parameters in file
    -v I<file>		library (I<file>)
    -y I<dir>		module_dir (I<dir>)
    all others		Put in returned list

The below list shows the GCC-like parameters that are supported,and the
functions that are called:

    -DI<var>=I<value>		define (I<var>,I<value>)
    -DI<var>		define (I<var>,undef)
    -UI<var>		undefine (I<var>)
    -II<dir>		incdir (I<dir>)
    -F I<file>		Parse parameters in file relatively
    -f I<file>		Parse parameters in file
    all others		Put in returned list

=item $self->write_parameters_file ( I<filename> )

Write the output from get_parameters to the specified file.

=back

=head1 ACCESSORS

=over 4

=item $self->define ( $token,$value )

This method is called when a define is recognized.  The default behavior
loads a hash that is used to fulfill define references.  This function may
also be called outside parsing to predefine values.

An optional third argument specifies parameters to the define,and a fourth
argument if true indicates the define was set on the command line and
should not be removed by `undefineall.

=item $self->define_names_sorted

Return sorted list of all define names that currently exist.

=item $self->defparams ( $token )

This method returns the parameter list of the define.  This will be defined,but false,if the define does not have arguments.

=item $self->defvalue ( $token )

This method returns the value of a given define,or prints a warning.

=item $self->defvalue_Nowarn ( $token )

This method returns the value of a given define,or undef.

=item $self->depend_files ()

Returns reference to list of filenames referenced with file_path,useful
for creating dependency lists.  With argument,adds that file.  With list
reference argument,sets the list to the argument.

=item $self->file_abs ( $filename )

Using the incdir and libext lists,convert the specified module or filename
("foo") to a absolute filename ("include/dir/foo.v").

=item $self->file_skip_special ( $filename )

Return true if the filename is one that generally should be ignored when
recursing directories,such as for example,".","CVS",and ".svn".

=item $self->file_substitute ( $filename )

Removes existing environment variables from the provided filename.  Any
undefined variables are not substituted nor cause errors.

=item $self->incdir ()

Returns reference to list of include directories.  With argument,adds that
directory.

=item $self->libext ()

Returns reference to list of library extensions.  With argument,adds that
extension.

=item $self->libext_matches (I<filename>)

Returns true if the passed filename matches the libext.

=item $self->library ()

Returns reference to list of libraries.  With argument,adds that library.

=item $self->module_dir ()

Returns reference to list of module directories.  With argument,adds that
directory.

=item $self->remove_defines ( $token )

Return string with any deFinitions in the token removed.

=item $self->undef ( $token )

Deletes a hash element that is used to fulfill define references.  This
function may also be called outside parsing to erase a predefined value.

=item $self->undefineall ()

Deletes all non-command line deFinitions,for implementing `undefineall.

=back

=head1 disTRIBUTION

Verilog-Perl is part of the L<http://www.veripool.org/> free Verilog EDA
software tool suite.  The latest version is available from CPAN and from
L<http://www.veripool.org/verilog-perl>.

copyright 2000-2010 by Wilson Snyder.  This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License Version 2.0.

=head1 AUTHORS

Wilson Snyder <wsnyder@wsnyder.org>

=head1 SEE ALSO

L<Verilog-Perl>,L<Verilog::Language>
=
cut

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐