Converted TmplTokenizer into a class. Everything still seems ok, but it is

not tested thoroughly.
This commit is contained in:
acli 2004-02-17 05:07:04 +00:00
parent ae87eee049
commit 39dc31c2c9
2 changed files with 208 additions and 72 deletions

View File

@ -12,7 +12,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
=head1 NAME
TmplTokenizer.pm - Simple-minded tokenizer for HTML::Template .tmpl files
TmplTokenizer.pm - Simple-minded tokenizer class for HTML::Template .tmpl files
=head1 DESCRIPTION
@ -31,16 +31,13 @@ on Ambrose's hideous Perl script known as subst.pl.
###############################################################################
$VERSION = 0.01;
$VERSION = 0.02;
@ISA = qw(Exporter);
@EXPORT_OK = qw();
use vars qw( $input );
use vars qw( $debug_dump_only_p );
use vars qw( $pedantic_attribute_error_in_nonpedantic_mode_p );
use vars qw( $pedantic_tmpl_var_use_in_nonpedantic_mode_p );
use vars qw( $fatal_p );
###############################################################################
@ -84,24 +81,155 @@ BEGIN {
# End of the hideous stuff
use vars qw( @readahead $lc_0 $lc $syntaxerror_p );
use vars qw( $cdata_mode_p $cdata_close );
use vars qw( $serial );
###############################################################################
# Easy accessors
sub FATAL_P () {'fatal-p'}
sub SYNTAXERROR_P () {'syntaxerror-p'}
sub fatal_p () {
return $fatal_p;
}
sub FILENAME () {'input'}
sub HANDLE () {'handle'}
sub syntaxerror_p () {
return $syntaxerror_p;
sub READAHEAD () {'readahead'}
sub LINENUM_START () {'lc_0'}
sub LINENUM () {'lc'}
sub CDATA_MODE_P () {'cdata-mode-p'}
sub CDATA_CLOSE () {'cdata-close'}
sub new {
my $this = shift;
my($input) = @_;
my $class = ref($this) || $this;
my $self = {};
bless $self, $class;
my $handle = sprintf('TMPLTOKENIZER%d', $serial);
$serial += 1;
no strict;
open($handle, "<$input") || die "$input: $!\n";
use strict;
$self->{+FILENAME} = $input;
$self->{+HANDLE} = $handle;
$self->{+READAHEAD} = [];
return $self;
}
###############################################################################
sub extract_attributes ($;$) {
# Simple getters
sub _handle {
my $this = shift;
return $this->{+HANDLE};
}
sub fatal_p {
my $this = shift;
return $this->{+FATAL_P};
}
sub syntaxerror_p {
my $this = shift;
return $this->{+SYNTAXERROR_P};
}
sub has_readahead_p {
my $this = shift;
return @{$this->{+READAHEAD}};
}
sub _peek_readahead {
my $this = shift;
return $this->{+READAHEAD}->[$#{$this->{+READAHEAD}}];
}
sub line_number_start {
my $this = shift;
return $this->{+LINENUM_START};
}
sub line_number {
my $this = shift;
return $this->{+LINENUM};
}
sub cdata_mode_p {
my $this = shift;
return $this->{+CDATA_MODE_P};
}
sub cdata_close {
my $this = shift;
return $this->{+CDATA_CLOSE};
}
# Simple setters
sub _set_fatal {
my $this = shift;
$this->{+FATAL_P} = $_[0];
return $this;
}
sub _set_syntaxerror {
my $this = shift;
$this->{+SYNTAXERROR_P} = $_[0];
return $this;
}
sub _push_readahead {
my $this = shift;
push @{$this->{+READAHEAD}}, $_[0];
return $this;
}
sub _pop_readahead {
my $this = shift;
return pop @{$this->{+READAHEAD}};
}
sub _append_readahead {
my $this = shift;
$this->{+READAHEAD}->[$#{$this->{+READAHEAD}}] .= $_[0];
return $this;
}
sub _set_readahead {
my $this = shift;
$this->{+READAHEAD}->[$#{$this->{+READAHEAD}}] = $_[0];
return $this;
}
sub _increment_line_number {
my $this = shift;
$this->{+LINENUM} += 1;
return $this;
}
sub _set_line_number_start {
my $this = shift;
$this->{+LINENUM_START} = $_[0];
return $this;
}
sub _set_cdata_mode {
my $this = shift;
$this->{+CDATA_MODE_P} = $_[0];
return $this;
}
sub _set_cdata_close {
my $this = shift;
$this->{+CDATA_CLOSE} = $_[0];
return $this;
}
###############################################################################
sub _extract_attributes ($;$) {
my $this = shift;
my($s, $lc) = @_;
my %attr;
$s = $1 if $s =~ /^<\S+(.*)\/\S$/s # XML-style self-closing tags
@ -139,7 +267,7 @@ sub extract_attributes ($;$) {
if ($s =~ /^([^\n]*)\n/s) { # this is even worse
error_normal("Completely confused while extracting attributes: $1", $lc);
error_normal((scalar(split(/\n/, $s)) - 1) . " more line(s) not shown.", undef);
$fatal_p = 1;
$this->_set_fatal( 1 );
} else {
warn_normal "Strange attribute syntax: $s\n", $lc;
}
@ -147,60 +275,66 @@ sub extract_attributes ($;$) {
return \%attr;
}
sub next_token_internal (*) {
sub _next_token_internal {
my $this = shift;
my($h) = @_;
my($it, $kind);
my $eof_p = 0;
pop @readahead if @readahead && !ref $readahead[$#readahead]
&& !length $readahead[$#readahead];
if (!@readahead) {
$this->_pop_readahead if $this->has_readahead_p
&& !ref $this->_peek_readahead
&& !length $this->_peek_readahead;
if (!$this->has_readahead_p) {
my $next = scalar <$h>;
$eof_p = !defined $next;
if (!$eof_p) {
$lc += 1;
push @readahead, $next;
$this->_increment_line_number;
$this->_push_readahead( $next );
}
}
$lc_0 = $lc; # remember line number of first line
if (@readahead && ref $readahead[$#readahead]) { # TmplToken object
my $t = pop @readahead;
($it, $kind, local $lc) = ($t->string, $t->type, $t->line_number);
} elsif ($eof_p && !@readahead) { # nothing left to do
$this->_set_line_number_start( $this->line_number ); # remember 1st line num
if ($this->has_readahead_p && ref $this->_peek_readahead) { # TmplToken obj.
($it, $kind) = ($this->_pop_readahead, undef);
} elsif ($eof_p && !$this->has_readahead_p) { # nothing left to do
;
} elsif ($readahead[$#readahead] =~ /^\s+/s) { # whitespace
($kind, $it, $readahead[$#readahead]) = (TmplTokenType::TEXT, $&, $');
} elsif ($this->_peek_readahead =~ /^\s+/s) { # whitespace
($kind, $it) = (TmplTokenType::TEXT, $&);
$this->_set_readahead( $' );
# FIXME the following (the [<\s] part) is an unreliable HACK :-(
} elsif ($readahead[$#readahead] =~ /^(?:[^<]|<[<\s])+/s) { # non-space normal text
($kind, $it, $readahead[$#readahead]) = (TmplTokenType::TEXT, $&, $');
warn_normal "Unescaped < in $it\n", $lc_0
if !$cdata_mode_p && $it =~ /</s;
} elsif ($this->_peek_readahead =~ /^(?:[^<]|<[<\s])+/s) { # non-space normal text
($kind, $it) = (TmplTokenType::TEXT, $&);
$this->_set_readahead( $' );
warn_normal "Unescaped < in $it\n", $this->line_number_start
if !$this->cdata_mode_p && $it =~ /</s;
} else { # tag/declaration/processing instruction
my $ok_p = 0;
for (;;) {
if ($cdata_mode_p) {
if ($readahead[$#readahead] =~ /^$cdata_close/) {
($kind, $it, $readahead[$#readahead]) = (TmplTokenType::TAG, $&, $');
for (my $cdata_close = $this->cdata_close;;) {
if ($this->cdata_mode_p) {
if ($this->_peek_readahead =~ /^$cdata_close/) {
($kind, $it) = (TmplTokenType::TAG, $&);
$this->_set_readahead( $' );
$ok_p = 1;
} else {
($kind, $it) = (TmplTokenType::TEXT, pop @readahead);
($kind, $it) = (TmplTokenType::TEXT, $this->_pop_readahead);
$ok_p = 1;
}
} elsif ($readahead[$#readahead] =~ /^$re_tag_compat/os) {
($kind, $it, $readahead[$#readahead]) = (TmplTokenType::TAG, "$1>", $3);
} elsif ($this->_peek_readahead =~ /^$re_tag_compat/os) {
($kind, $it) = (TmplTokenType::TAG, "$1>");
$this->_set_readahead( $3 );
$ok_p = 1;
warn_normal "SGML \"closed start tag\" notation: $1<\n", $lc_0 if $2 eq '';
} elsif ($readahead[$#readahead] =~ /^<!--(?:(?!-->).)*-->/s) {
($kind, $it, $readahead[$#readahead]) = (TmplTokenType::COMMENT, $&, $');
warn_normal "SGML \"closed start tag\" notation: $1<\n", $this->line_number_start if $2 eq '';
} elsif ($this->_peek_readahead =~ /^<!--(?:(?!-->).)*-->/s) {
($kind, $it) = (TmplTokenType::COMMENT, $&);
$this->_set_readahead( $' );
$ok_p = 1;
warn_normal "Syntax error in comment: $&\n", $lc_0;
$syntaxerror_p = 1;
warn_normal "Syntax error in comment: $&\n", $this->line_number_start;
$this->_set_syntaxerror( 1 );
}
last if $ok_p;
my $next = scalar <$h>;
$eof_p = !defined $next;
last if $eof_p;
$lc += 1;
$readahead[$#readahead] .= $next;
$this->_increment_line_number;
$this->_append_readahead( $next );
}
if ($kind ne TmplTokenType::TAG) {
;
@ -210,44 +344,46 @@ sub next_token_internal (*) {
} elsif ($it =~ /^<\?/) {
$kind = TmplTokenType::PI;
}
if ($it =~ /^$re_directive/ios && !$cdata_mode_p) {
if ($it =~ /^$re_directive/ios && !$this->cdata_mode_p) {
$kind = TmplTokenType::DIRECTIVE;
}
if (!$ok_p && $eof_p) {
($kind, $it, $readahead[$#readahead]) = (TmplTokenType::UNKNOWN, $readahead[$#readahead], undef);
$syntaxerror_p = 1;
($kind, $it) = (TmplTokenType::UNKNOWN, $this->_peek_readahead);
$this->_set_readahead, undef;
$this->_set_syntaxerror( 1 );
}
}
warn_normal "Unrecognizable token found: $it\n", $lc_0
warn_normal "Unrecognizable token found: $it\n", $this->line_number_start
if $kind eq TmplTokenType::UNKNOWN;
return defined $it? TmplToken->new($it, $kind, $lc): undef;
return defined $it? (ref $it? $it: TmplToken->new($it, $kind, $this->line_number)): undef;
}
sub next_token (*) {
my($h) = @_;
sub next_token {
my $this = shift;
my $h = $this->_handle;
my $it;
if (!$cdata_mode_p) {
$it = next_token_internal($h);
if (!$this->cdata_mode_p) {
$it = $this->_next_token_internal($h);
if (defined $it && $it->type eq TmplTokenType::TAG) {
($cdata_mode_p, $cdata_close) = (1, "</$1\\s*>")
if $it->string =~ /^<(script|style|textarea)\b/i;
$it->set_attributes( extract_attributes($it->string, $lc_0) );
if ($it->string =~ /^<(script|style|textarea)\b/i) {
$this->_set_cdata_mode( 1 );
$this->_set_cdata_close( "</$1\\s*>" );
}
$it->set_attributes( $this->_extract_attributes($it->string, $it->line_number) );
}
} else {
for ($it = '';;) {
my $lc_prev = $lc;
my $next = next_token_internal($h);
for ($it = '', my $cdata_close = $this->cdata_close;;) {
my $next = $this->_next_token_internal($h);
last if !defined $next;
if (defined $next && $next->string =~ /$cdata_close/i) {
push @readahead, $next; # push the entire TmplToken object
#$lc = $lc_prev; XXX
$cdata_mode_p = 0;
$this->_push_readahead( $next ); # push entire TmplToken object
$this->_set_cdata_mode( 0 );
}
last unless $cdata_mode_p;
last unless $this->cdata_mode_p;
$it .= $next->string;
}
$it = TmplToken->new( $it, TmplTokenType::CDATA, $lc );
$cdata_close = undef;
$it = TmplToken->new( $it, TmplTokenType::CDATA, $this->line_number );
$this->_set_cdata_close, undef;
}
return $it;
}

View File

@ -25,7 +25,7 @@ use vars qw( $pedantic_p );
###############################################################################
sub debug_dump (*) { # for testing only
sub debug_dump ($) { # for testing only
my($h) = @_;
print "re_tag_compat is /", TmplTokenizer::re_tag(1), "/\n";
for (;;) {
@ -50,7 +50,7 @@ sub debug_dump (*) { # for testing only
###############################################################################
sub text_extract (*) {
sub text_extract ($) {
my($h) = @_;
my %text = ();
for (;;) {
@ -124,11 +124,11 @@ VerboseWarnings::set_pedantic_mode $pedantic_p;
usage_error('Missing mandatory option -f') unless defined $input;
open(INPUT, "<$input") || die "$0: $input: $!\n";
my $h = TmplTokenizer->new( $input );
if ($debug_dump_only_p) {
debug_dump(*INPUT);
debug_dump( $h );
} else {
text_extract(*INPUT);
text_extract( $h );
}
warn "This input will not work with Mozilla standards-compliant mode\n", undef