# See copyright, etc in below POD section.
######################################################################
package SystemC::Vregs::Language;
use strict;
use vars qw($VERSION $Global_Change_Error);
use Carp;
use IO::File;
$VERSION = '1.470';
# Set to globally report if any files change;
# for local usage, use new(change_error => ...) instead.
#$Global_Change_Error = undef;
######################################################################
#### Implementation
######################################################################
#### Creation
sub new {
my $class = shift;
my $self = {text=>[],
close_text=>[],
#keep_timestamp=>undef,
#dry_run=>undef, # Don't do it, just see if would do it
#change_diff=>"",
#change_error=>{},
#changes=>undef, # For dry_run, any changes found?
#verbose=>0,
@_};
$self->{filename} or croak "%Error: ->new() requires filename=> argument, stopped";
$self->{modulename} = $self->{filename};
$self->{modulename} =~ s/.*[\/\\]//;
$self->{modulename} =~ s/[^a-zA-Z0-9]/_/g;
my $bless_class = $class;
if ($self->{language}) {
# Have language=>C use the class SystemC::Vregs::Language::C
$bless_class .= "::" . $self->{language};
my $package_class = __PACKAGE__ . "::" . $self->{language};
$package_class = $class if $class eq __PACKAGE__;
#exists $::{$bless_class} or croak "%Error: ->new() passed invalid language=>",$self->{language},", stopped";
# Things are interesting, because the user might have made their
# own class. We'll simply make a multiple-inheritance package for them.
eval ("
package ${bless_class};
use base qw (${package_class} ${class});
1;") or die;
}
# Allow $self->{C} to be a short cut for $self->{language} eq "C"
$self->{ $self->{language} } = 1;
bless $self, $bless_class;
return $self;
}
sub DESTROY {
my $self = shift;
if ($#{$self->{text}} >= 0) {
$self->close();
}
}
sub close_prep {
my $self = shift;
$self->print (@{$self->{close_text}});
@{$self->{close_text}} = ();
}
sub text_to_output {
my $self = shift;
return join('',@{$self->{text}},@{$self->{close_text}});
}
sub close {
my $self = shift;
return if $self->{_closing}; # Don't recurse in close if we die() here.
$self->{_closing} = 1;
$self->close_prep();
my @oldtext; # Old file contents
my $keepstamp = $self->{keep_timestamp};
if ($keepstamp) {
my $fh = IO::File->new ($self->{filename});
if ($fh) {
@oldtext = $fh->getlines();
$fh->close();
} else {
$keepstamp = 0;
}
}
if (!$keepstamp
|| (join ('',@oldtext) ne join ('',@{$self->{text}}))) {
$self->{changes} = 1;
my $diff = describe_diff (join ('',@oldtext), join ('',@{$self->{text}}));
if ($Global_Change_Error) {
die "%Error: Changes needed to $self->{filename}, but globally not allowed to change files\n$diff\n";
} elsif ($self->{change_error}{ $self->language }
|| $self->{change_error}{ALL}) {
if ($self->_close_change_diff()) {
die "%Error: Changes needed to $self->{filename}, but not allowed to change ".$self->language." files\n$diff\n";
}
}
if ($self->{dry_run}) {
printf "Would write $self->{filename} (--dry-run)\n" if ($self->{verbose});
} else {
printf "Writing $self->{filename}\n" if ($self->{verbose});
my $fh = IO::File->new ($self->{filename},"w") or die "%Error: $! $self->{filename}\n";
print $fh @{$self->{text}};
$fh->close();
}
} else {
printf "Same $self->{filename}\n" if ($self->{verbose});
}
$self->{text} = [];
delete $self->{_closing};
}
sub describe_diff {
my $text1 = shift;
my $text2 = shift;
my @l1 = split(/\n/, $text1);
my @l2 = split(/\n/, $text2);
my $diffs = "";
my $nl = $#l1; $nl = $#l2 if ($#l2 > $nl);
for (my $l=0; $l<=$nl; $l++) {
if (($l1[$l]||"") ne ($l2[$l]||"")) {
$diffs .= "- ".$l1[$l]."\n" if defined $l1[$l];
$diffs .= "+ ".$l2[$l]."\n" if defined $l2[$l];
}
}
return $diffs;
}
our $_CloseUnlink; END { unlink($_CloseUnlink) if $_CloseUnlink; }
sub _close_change_diff {
my $self = shift;
# Are there differences the user cared about?
return 1 if (!$self->{change_diff});
# Write to temp file
my $tempname = (($ENV{TEMP}||$ENV{TMP}||"/tmp")."/.vreg_".$$);
$_CloseUnlink = $tempname;
my $fh = IO::File->new(">$tempname") or die "%Error: $! $tempname,";
$fh or die "%Error: $! $tempname\n";
print $fh @{$self->{text}};
$fh->close();
# Diff it
system ($self->{change_diff}, $self->{filename}, $tempname);
my $status = $?;
# Cleanup
unlink ($tempname); $_CloseUnlink=undef;
return ($status != 0);
}
######################################################################
#### Accessors
sub language {
my $self = shift;
return $self->{language};
}
sub is_keyword {
my $sym = shift;
return (SystemC::Vregs::Language::C::is_keyword($sym) && "C"
|| SystemC::Vregs::Language::CPP::is_keyword($sym) && "CPP"
|| SystemC::Vregs::Language::Perl::is_keyword($sym) && "Perl"
|| SystemC::Vregs::Language::Verilog::is_keyword($sym) && "Verilog"
|| SystemC::Vregs::Language::Assembler::is_keyword($sym) && "Assembler"
|| SystemC::Vregs::Language::Tcl::is_keyword($sym) && "Tcl"
# XML keywords can't conflict as they all have <'s
);
}
######################################################################
#### Printing
sub push_text {
my $self = shift;
push @{$self->{text}}, @_;
}
sub print {
my $self = shift;
$self->push_text(@_);
}
sub printf {
my $self = shift;
my $fmt = shift;
local $SIG{__WARN__} = sub { carp @_ };
my $text = sprintf ($fmt, @_);
$self->print($text);
}
sub push_close_text {
my $self = shift;
push @{$self->{close_text}}, @_;
}
sub print_at_close {
my $self = shift;
$self->push_close_text(@_);
}
sub printf_at_close {
my $self = shift;
my $fmt = shift;
push @{$self->{close_text}}, sprintf ($fmt, @_);
}
sub comment {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
my $strg = join ('', @_);
# Assume C++ style commenting
$strg =~ s%\n(?!$)% */\n/*%sg;
if ($strg =~ s/\n$//) {
$self->print("/* $strg */\n");
} else {
$self->print("/* $strg */");
}
}
sub comment_pre {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
$self->comment(@_);
}
sub comment_post {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
$self->comment(@_);
}
sub define {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
my $def = shift;
my $val = shift;
my $cmt = shift;
# Assume C++ define
my $len = ((length($val)> 16) ? 29 : 16);
if ($cmt) {
$self->printf ("#define\t%-26s\t%${len}s\t", $def, $val);
$self->comment_post ($cmt,"\n");
} else {
$self->printf ("#define\t%-26s\t%${len}s\n", $def, $val);
}
}
sub preproc_char {
return '#';
}
sub preproc {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
$self->print($self->preproc_char(), @_);
}
sub include_guard {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
my $cmt = "_".uc $self->{modulename}."_";
if ($self->{language} eq 'Verilog') {
$self->preproc ("ifdef $cmt\n");
$self->preproc ("else\n"); # Verilog doesn't support ifndef
} else {
$self->preproc ("ifndef $cmt\n");
}
$self->preproc ("define $cmt 1\n");
$self->print_at_close("\n", $self->preproc_char(), "endif /*$cmt*/\n");
}
sub sprint_hex_value {
my ($self,$value,$bits,$force_ull) = @_;
if ($bits>32) {
if ($force_ull) {
return "0x".$value . "ULL";
} else {
return "VREGS_ULL(0x".$value . ")";
}
} else {
return "0x".$value;
}
}
sub sprint_hex_value_add0 {
my ($self,$valuestr,$bits) = @_;
# Print the hex number, adding leading 0s to make it the proper width
$valuestr = "0".$valuestr; # Force conversion to string in case is Bit::Vector
$valuestr=~ s/^0+([0-9a-f])/$1/i;
my $add = int(($bits+3)/4) - length($valuestr);
$valuestr = "0"x$add . $valuestr if $add>=1;
#print "ADD $valuestr $add ".("0"x$add)."\n" if $SystemC::Vregs::Debug;
return $self->sprint_hex_value ($valuestr, $bits);
}
sub sprint_hex_value_drop0 {
my ($self,$valuestr,$bits) = @_;
$valuestr = "0".$valuestr; # Force conversion to string in case is Bit::Vector
$valuestr=~ s/^0+(\d)/$1/;
return $self->sprint_hex_value ($valuestr, $bits);
}
######################################################################
######################################################################
######################################################################
#### C
package SystemC::Vregs::Language::C;
use Carp;
use vars qw(%Keywords);
#Made by super::New: use base qw(SystemC::Vregs::Language);
use strict;
#Includes some stdlib functions at the end.
foreach my $kwd (qw( asm auto break case catch cdecl char class const
continue default delete do double else enum extern far
float for friend goto huge if inline int interrupt
long near new operator pascal private protected public
register short signed sizeof static struct switch
template this throw try typedef union unsigned virtual
void volatile while
bool false NULL string true
sensitive sensitive_pos sensitive_neg
abort))
{ $Keywords{$kwd} = 1; }
sub is_keyword {
return $Keywords{$_[0]};
}
sub comment_pre {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
my $strg = join ('', @_);
$strg =~ s!\n(.)!\n/// $1!og;
$strg =~ s!\n\n!\n///\n!og;
$strg = " ".$strg unless $strg =~ /^\s/;
$self->print("///$strg");
}
sub comment_post {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
my $strg = join ('', @_);
$strg =~ s!\n(.)!\n///< $1!og;
$self->print("///< $strg");
}
######################################################################
######################################################################
######################################################################
#### CPP
package SystemC::Vregs::Language::CPP;
use Carp;
use vars qw(%Keywords);
use base qw(SystemC::Vregs::Language::C);
use strict;
sub is_keyword {
return SystemC::Vregs::Language::C::is_keyword(@_);
}
######################################################################
######################################################################
######################################################################
#### Lisp
package SystemC::Vregs::Language::Lisp;
use base qw(SystemC::Vregs::Language);
use Carp;
use strict;
sub is_keyword { return undef;}
sub include_guard {}
sub comment_start_char { return ";;"; }
sub comment_end_char { return ""; }
sub comment {
my $self = shift;
my $strg = join ('', @_);
$strg =~ s!\n(.)!\n;;$1!g;
$strg =~ s!\n\n!\n;;\n!og;
$self->print(";;".$strg);
}
sub define {
my $self = shift; ($self && ref($self)) or croak 'Not a hash reference';
my $def = shift;
my $val = shift;
my $cmt = shift;
if ($cmt) {
$self->printf ("(defconstant %-26s\t%16s) ;; %s\n", $def, $val, $cmt);
} else {
$self->printf ("(defconstant %-26s\t%16s)\n", $def, $val);
}
}
sub sprint_hex_value {
my ($self,$value,$bits) = @_;
return "#x".$value;
}
######################################################################
######################################################################
######################################################################
#### Perl
package SystemC::Vregs::Language::Perl;
#Made by super::New: use base qw(SystemC::Vregs::Language);
use strict;
sub is_keyword {
my $sym = shift;
return undef;
}
sub include_guard {
my $self = shift;
# Presumably is a module, so doesn't matter
$self->printf_at_close ("1;\n"); # Good idea to have true exit status though.
}
sub comment_start_char {
return "#";
}
sub comment_end_char {
return "";
}
sub comment {
my $self = shift;
my $strg = join ('', @_);
$strg =~ s!\n(.)!\n#$1!g;
$strg =~ s!\n\n!\n#\n!og;
$self->print("#".$strg);
}
sub preproc {
my $self = shift;
warn 'No preprocessor for Perl Language';
}
sub define {
my $self = shift;
if ($_[2]) {
$self->printf ("use constant %-26s\t=> %16s;\t# %s\n", @_);
} else {
$self->printf ("use constant %-26s\t=> %16s;\n", @_);
}
}
sub sprint_hex_value {
my ($self,$value,$bits) = @_;
if ($bits>32) {
# return "Bit::Vector::new_hex(".$bits.",0x".$value.")";
return "0x".$value;
} else {
return "0x".$value;
}
}
######################################################################
######################################################################
######################################################################
#### Verilog
package SystemC::Vregs::Language::Verilog;
#Made by super::New: use base qw(SystemC::Vregs::Language);
use strict;
use Verilog::Language;
sub is_keyword {
my $sym = shift;
return (Verilog::Language::is_keyword($sym));
}
sub preproc_char {
return "`";
}
sub define {
my $self = shift;
if ($_[2]) {
$self->printf ("`define\t%-26s\t%16s\t// %s\n", @_);
} else {
$self->printf ("`define\t%-26s\t%16s\n", @_);
}
}
sub sprint_hex_value {
my ($self,$value,$bits) = @_;
return "${bits}'h".$value;
}
######################################################################
######################################################################
######################################################################
#### Assembler
package SystemC::Vregs::Language::Assembler;
#Made by super::New: use base qw(SystemC::Vregs::Language);
use strict;
sub is_keyword {
my $sym = shift;
return undef;
}
sub comment_start_char {
return ";";
}
sub comment_end_char {
return "";
}
sub comment {
my $self = shift;
my $strg = join ('', @_);
$strg =~ s!\n(.)!\n;$1!g;
$strg =~ s!\n\n!\n;\n!og;
$self->print (";".$strg);
}
######################################################################
######################################################################
######################################################################
#### Gas Assembler
package SystemC::Vregs::Language::Gas;
use base qw(SystemC::Vregs::Language);
use strict;
sub is_keyword {
my $sym = shift;
return undef;
}
sub sprint_hex_value {
my ($self,$value,$bits) = @_;
# Never a ULL postfix
return "0x".$value;
}
######################################################################
######################################################################
######################################################################
#### Tcl
package SystemC::Vregs::Language::Tcl;
use base qw(SystemC::Vregs::Language);
use strict;
sub is_keyword {
my $sym = shift;
return undef;
}
sub comment_start_char {
return "\#";
}
sub comment_end_char {
return "";
}
sub comment {
my $self = shift;
my $strg = join ('', @_);
$strg =~ s!\n(.)!\n#$1!g;
$strg =~ s!\n\n!\n#\n!og;
$self->print ("\#".$strg);
}
######################################################################
######################################################################
######################################################################
#### XML
package SystemC::Vregs::Language::XML;
use base qw(SystemC::Vregs::Language);
use strict;
sub is_keyword { return undef;}
sub comment_start_char { return "<!--"; }
sub comment_end_char { return "-->"; }
sub comment {
my $self = shift;
my $strg = join ('', @_);
$strg =~ s%--+%-%sg; # Drop --'s, they end comments
$strg =~ s%[<>]%_%sg; # Replace special <>'s
$strg =~ s%\n(?!$)% -->\n<!-- %sg;
if ($strg =~ s/\n$//) {
$self->print("<!-- $strg -->\n");
} else {
$self->print("<!-- $strg -->");
}
}
######################################################################
package SystemC::Vregs::Language;
#### Package return
1;
=pod
=head1 NAME
SystemC::Vregs::Language - File processing for various Languages
=head1 SYNOPSIS
use SystemC::Vregs::Language;
my $fh = SystemC::Vregs::Language->new (filename=>"foo.c",
language=>'C',);
$fh->comment ("This file is generated automatically\n");
$fh->define ("TRUE",1, "Set true");
$fh->print ("void main();\n");
=head1 DESCRIPTION
This package creates a file handle with language specific semantics. This
allows similar operators to be called, such as I<comment>, for many
different file formats.
The output data is stored in an array and dumped when the file is complete.
This allows the file to only be written if the data changes, to reduce
makefile rebuilding.
=head1 FIELDS
These fields may be specified with the new() function.
=over 4
=item filename
The filename to write the data to.
=item keep_timestamp
If true, the file will only be written if the data being written differs
from the present file contents.
=item language
The language for the file. May be C, Perl, Assembler, TCL, or Verilog. A new
language Foo may be defined by making a SystemC::Vregs::Language::Foo class
which is an @ISA of SystemC::Vregs::Language.
=back
=head1 ACCESSORS
=over 4
=item language
Returns the type of file, for example 'C'.
=back
=head1 OUTPUT FUNCTIONS
=over 4
=item comment
Output a string with the appropriate comment delimiters.
=item comment_pre
Output a comment and Doxygen document before-the-fact.
=item comment_post
Output a comment and Doxygen document after-the-fact.
=item include_guard
Output a standard #ifndef around the file to prevent multiple inclusion.
Closing the file will automatically add the #endif
=item sprint_hex_value
Return a string representing the value as a hex number. Second argument is
number of bits.
=item preproc
Output a preprocessor directive.
=item print
Output plain text. This function is called by all other functions. You
will probably want to make a inherited class and override this method.
=item printf
Output printf text.
=back
=head1 DISTRIBUTION
Vregs is part of the L<http://www.veripool.org/> free Verilog software tool
suite. The latest version is available from CPAN and from
L<http://www.veripool.org/vregs>. /www.veripool.org/>.
Copyright 2001-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<SystemC::Vregs>, L<IO::File>
=cut