# Copyright (C) 1998-09 Stephane Galland # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. =pod =head1 NAME Bib2HTML::Parser::Parser - A parser for BibTeX files. =head1 SYNOPSYS use Bib2HTML::Parser::Parser ; my $gen = Bib2HTML::Parser::Parser->new() ; =head1 DESCRIPTION Bib2HTML::Parser::Parser is a Perl module, which parses a source file to recognize the BibTeX tokens. =head1 GETTING STARTED =head2 Initialization To create a parser, say something like this: use Bib2HTML::Parser::Parser; my $parser = Bib2HTML::Parser::Parser->new() ; ...or something similar. The constructor could take 1 arg: =over =item * show_bibtex (boolean) indicates if the BibTeX code will be generated inside a verbatim area. This flag permits to this parser to store (or not) the initial value of each field. Caution about the memory space. =head1 METHOD DESCRIPTIONS This section contains only the methods in Parser.pm itself. =over =cut package Bib2HTML::Parser::Parser; @ISA = ('Exporter'); @EXPORT = qw(); @EXPORT_OK = qw(); use strict; use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION); use Exporter; use Carp ; use File::Basename ; use File::Spec ; use Bib2HTML::General::Misc ; use Bib2HTML::General::Error ; use Bib2HTML::General::Verbose ; use Bib2HTML::Parser::BibScanner ; use Bib2HTML::Translator::TeX ; use Bib2HTML::Translator::BibTeXEntry ; #------------------------------------------------------ # # Global vars # #------------------------------------------------------ # Version number of the parser my $VERSION = "2.1" ; #------------------------------------------------------ # # Constructor # #------------------------------------------------------ sub new(;$) : method { my $proto = shift; my $class = ref($proto) || $proto; my $parent = ref($proto) && $proto ; my $self ; if ( $parent ) { %{$self} = %{$parent} ; bless( $self, $class ); } else { $self = { 'CONTENT' => {}, 'FILE_PREAMBLES' => '', 'SHOW_BIBTEX' => $_[0], } ; bless( $self, $class ); $self->clearcontent() ; } return $self; } #------------------------------------------------------ # # Getter/setter functions # #------------------------------------------------------ =pod =item * content() Replies the content of the documentation read by the parser. =cut sub content() : method { my $self = shift ; return $self->{'CONTENT'} ; } =pod =item * clearcontent() Destoys the readed content and sets it to the empty. =cut sub clearcontent() : method { my $self = shift ; $self->{'CONTENT'} = { } ; } #------------------------------------------------------ # # Main parsing functions # #------------------------------------------------------ =pod =item * parse() Parses the files. Takes 1 arg: =over =item * file_list (array ref) is an array that contains the names of the files and the directories from which the parser must read the bibtex entries. =back =cut sub parse(\@) : method { my $self = shift ; $self->clearcontent() ; # Evaluate the TeX expressions if ( ( $self->{'FILE_PREAMBLES'} ) && ( exists( $self->{'FILE_PREAMBLES'}{'tex'} ) ) && ( $self->{'FILE_PREAMBLES'}{'tex'} ) ) { Bib2HTML::General::Verbose::two( "Evaluate the user's LaTeX definitions...\n" ) ; my $trans = Bib2HTML::Translator::TeX->new( $self->{'FILE_PREAMBLES'}{'filename'} ) ; $trans->translate( $self->{'FILE_PREAMBLES'}{'tex'}, $self->{'CONTENT'}{'entries'} ) ; } # Evaluate each file if ( isarray( $_[0] ) ) { foreach my $file (@{$_[0]}) { $self->readfile( $file ) ; } } else { die("END1"); $self->readfile( $_[0] ) ; } # Save the TeX entries if ( $self->{'SHOW_BIBTEX'} ) { $self->save_bibtex_entries() ; } # Evalute TeX expressions $self->tex_translation() ; # Expand the crossref fields $self->expand_crossref() ; return $self->content() ; } =pod =item * readfile() Reads the content of a file. Takes 1 arg: =over =item * name (string) is the name of the file to read. =back =cut sub readfile($) : method { my $self = shift ; my $filename = $_[0] || confess( 'you must supply the filename' ) ; # Read the file content Bib2HTML::General::Verbose::two( "Read $filename..." ) ; my $scanner = new Bib2HTML::Parser::BibScanner() ; if ($self->{'CONTENT'}) { my $content = $scanner->scanentries($filename) ; # Merge the constants if ($content->{'constants'}) { foreach my $constant (keys %{$content->{'constants'}}) { if (exists $self->{'CONTENT'}{'constants'}{"$constant"}) { Bib2HTML::General::Error::warm( "multiple definition for the constant '". $constant."'", $filename, extract_line_from_location($content->{'constants'}{"$constant"}{'location'})) ; } $self->{'CONTENT'}{'constants'}{"$constant"} = $content->{'constants'}{"$constant"} ; } } # Merge the preambles if ($content->{'preambles'}) { push @{$self->{'CONTENT'}{'preambles'}}, @{$content->{'preambles'}} ; } # Merge the comments if ($content->{'comments'}) { push @{$self->{'CONTENT'}{'comments'}}, @{$content->{'comments'}} ; } # Merge the red content to the previous content if ($content->{'entries'}) { foreach my $entry (keys %{$content->{'entries'}}) { if (exists $self->{'CONTENT'}{'entries'}{"$entry"}) { Bib2HTML::General::Error::warm( "multiple definition for the entry '". $entry."'", $filename, extract_line_from_location($content->{'entriess'}{"$entry"}{'location'})) ; } $self->{'CONTENT'}{'entries'}{"$entry"} = $content->{'entries'}{"$entry"} ; } } } else { $self->{'CONTENT'} = $scanner->scanentries($filename) ; } } =pod =item * read_preambles() Read some LaTeX commands from a file. There commands are defined previously any command from the BibTeX files. Takes 1 arg: =over =item * file (string) =back =cut sub read_preambles($) : method { my $self = shift ; $self->{'FILE_PREAMBLES'} = '' ; if ( $_[0] ) { Bib2HTML::General::Verbose::two( "Reading the user's LaTeX definitions...\n" ) ; if ( ( -f $_[0] ) && ( -r $_[0] ) ) { my $tex = '' ; local *FILE_PREAMBLE ; open( *FILE_PREAMBLE, "< $_[0]" ) or Bib2HTML::General::Error::syserr( "$_[0]: $!" ) ; while ( my $line = ) { $tex .= $line ; } close( *FILE_PREAMBLE ) ; $tex =~ s/^[ \t\n\r]+// ; $tex =~ s/[ \t\n\r]+$// ; if ( $tex ) { $self->{'FILE_PREAMBLES'} = {} ; $self->{'FILE_PREAMBLES'}{'tex'} = $tex ; $self->{'FILE_PREAMBLES'}{'filename'} = $_[0] ; } } else { Bib2HTML::General::Error::syserr( "unable to find or read the preamble file '$_[0]'" ) ; } } } =pod =item * expand_crossref() Expands crossref fields. =cut sub expand_crossref() : method { my $self = shift ; Bib2HTML::General::Verbose::two( "Expand crossref fields...\n" ) ; foreach my $entry (keys %{$self->{'CONTENT'}{'entries'}}) { if ( exists $self->{'CONTENT'}{'entries'}{"$entry"}{'fields'}{'crossref'} ) { my $parent = $self->{'CONTENT'}{'entries'}{"$entry"}{'fields'}{'crossref'} ; if ( exists $self->{'CONTENT'}{'entries'}{"$parent"} ) { foreach my $field (keys %{$self->{'CONTENT'}{'entries'}{"$parent"}{'fields'}}) { if ( ! exists $self->{'CONTENT'}{'entries'}{"$entry"}{'fields'}{"$field"} ) { $self->{'CONTENT'}{'entries'}{"$entry"}{'fields'}{"$field"} = $self->{'CONTENT'}{'entries'}{"$parent"}{'fields'}{"$field"} ; } } } else { my $filename = extract_file_from_location( $self->{'CONTENT'}{'entries'}{"$entry"}{'location'} ) ; my $lineno = extract_line_from_location( $self->{'CONTENT'}{'entries'}{"$entry"}{'location'} ) ; Bib2HTML::General::Error::warm( "unable to find the entry '$parent' required for ". "the crossref from '$entry'", $filename, $lineno ) ; } } } } =pod =item * tex_translation() Translates TeX expressions =cut sub tex_translation() : method { my $self = shift ; # Evaluate @preamble Bib2HTML::General::Verbose::two( "Evaluate TeX expressions for the preambles...\n" ) ; foreach my $preamble (@{$self->{'CONTENT'}{'preambles'}}) { if ( $preamble->{'tex'} ) { my $trans = Bib2HTML::Translator::TeX->new( extract_file_from_location( $preamble->{'location'} ) ) ; $trans->translate( $preamble->{'tex'}, $self->{'CONTENT'}{'entries'}, extract_line_from_location( $preamble->{'location'} ) ) ; } } # Evaluate @string Bib2HTML::General::Verbose::two( "Evaluate TeX expressions for the constants...\n" ) ; my $be = new Bib2HTML::Translator::BibTeXEntry() ; foreach my $const (keys %{$self->{'CONTENT'}{'constants'}}) { $self->{'CONTENT'}{'constants'}{$const}{'text'} = $be->expand_bibtex_vars($self->{'CONTENT'}{'constants'}{$const}{'text'}, $self->{'CONTENT'}{'constants'}) ; } # Evaluate @preamble Bib2HTML::General::Verbose::two( "Evaluate TeX expressions for the preambles...\n" ) ; # Evaluate entries' fields foreach my $entry (keys %{$self->{'CONTENT'}{'entries'}}) { Bib2HTML::General::Verbose::two( "Evaluate TeX expressions for '$entry'...\n" ) ; my $filename = extract_file_from_location( $self->{'CONTENT'}{'entries'}{$entry}{'location'} ) ; my $lineno = extract_line_from_location( $self->{'CONTENT'}{'entries'}{$entry}{'location'} ) ; my $trans = Bib2HTML::Translator::TeX->new( $filename ) ; foreach my $field (keys %{$self->{'CONTENT'}{'entries'}{$entry}{'fields'}}) { if ( ! bibtex_ignore_field_parsing( $field ) ) { # Translate the TeX code of this field into HTML source code Bib2HTML::General::Verbose::three( "\ttreat field '$field'" ) ; my $text = $be->expand_bibtex_vars($self->{'CONTENT'}{'entries'}{$entry}{'fields'}{$field}, $self->{'CONTENT'}{'constants'} ) ; $self->{'CONTENT'}{'entries'}{$entry}{'fields'}{$field} = $trans->translate( $text, $self->{'CONTENT'}{'entries'}, $lineno ) ; } else { # Do not translate the TeX code of this field into HTML source code # Simply remove the if ( $self->{'CONTENT'}{'entries'}{$entry}{'fields'}{$field} =~ /^{(.*)}$/) { $self->{'CONTENT'}{'entries'}{$entry}{'fields'}{$field} = "$1" ; } } } } } =pod =item * save_bibtex_entries() Saves the original text for the BibTeX entries =cut sub save_bibtex_entries() : method { my $self = shift ; Bib2HTML::General::Verbose::two( "Save the BibTeX original values for futher verbatim...\n" ) ; # Evaluate entries' fields foreach my $entry (keys %{$self->{'CONTENT'}{'entries'}}) { foreach my $field (keys %{$self->{'CONTENT'}{'entries'}{$entry}{'fields'}}) { $self->{'CONTENT'}{'entries'}{$entry}{'original-fields'}{$field} = $self->{'CONTENT'}{'entries'}{$entry}{'fields'}{$field} ; } } } 1; __END__ =back =head1 COPYRIGHT (c) Copyright 1998-09 Stéphane Galland , under GPL. =head1 AUTHORS =over =item * Conceived and initially developed by Stéphane Galland Egalland@arakhne.orgE. =back =head1 SEE ALSO bib2html.pl