\n";} print ${$count}[1]."+".${$count}[2]."+".${$count}[3]. " (".${$count}[4]."/".${$count}[5]."/".${$count}[6]."/".${$count}[7].") ". $header; if ($htmlstyle) {print "
\n";} return; } if ($htmlstyle) {print "\n";} while (!($tex->{'eof'})) { parse_unit($tex,1); } if ($htmlstyle && $verbose) {print "
\n";} } sub parse_unit { # Status: # 0 = exclude from count # 1 = text # 2 = header text # 3 = float text # -1 = float (exclude) # -2 = strong exclude, ignore begin-end groups # -3 = stronger exclude, do not parse macro parameters # -9 = preamble (between \documentclass and \begin{document}) my ($tex,$status,$end)=@_; if (!defined $status) { print_error("CRITICAL ERROR: Undefined parser status!"); exit; } elsif (ref($status) eq 'ARRAY') { print_error("CRITICAL ERROR: Invalid parser status!"); exit; } my $count=$tex->{'count'}; my $substat; if ($showstates) { if (defined $end) { $tex->{'printstate'}=':'.$status.':'.$end.':'; } else { $tex->{'printstate'}=':'.$status.':'; } flush_next($tex); } while (defined (my $next=next_token($tex))) { # parse next token; or tokens until match with $end set_style($tex,"ignore"); if ((defined $end) && ($end eq $next)) { # end of unit return; } elsif (!defined $next) { print_error("ERROR: End of file while waiting for ".$end); return; } if ($status==-9 && $next eq '\begin' && $tex->{'line'}=~/^\{\s*document\s*\}/) { # \begin{document} $status=1; } if ($next eq '\documentclass') { # starts preamble set_style($tex,'document'); gobble_option($tex); gobble_macro_parms($tex,1); while (!($tex->{'eof'})) { parse_unit($tex,-9); } } elsif ($tex->{'type'}==666) { # parse TC instructions parse_tc($tex); } elsif ($tex->{'type'}==1) { # word if ($status>0) { ${$count}[$status]++; set_style($tex,'word'.$status); } } elsif ($next eq '{') { # {...} parse_unit($tex,$status,'}'); } elsif ($tex->{'type'}==3 && $status==-3) { set_style($tex,'ignore'); } elsif ($tex->{'type'}==3) { # macro call set_style($tex,'command'); if ($next eq '\begin' && $status!=-2) { parse_begin_end($tex,$count,$status); } elsif (($status==-1) && ($substat=$TeXfloatinc{$next})) { # text included from float gobble_macro_parms($tex,$substat); } elsif ($status==-9 && defined ($substat=$TeXpreamble{$next})) { # parse preamble include macros if (defined $TeXheader{$next}) {${$count}[4]++;} gobble_macro_parms($tex,$substat,1); } elsif ($status<0) { # ignore gobble_option($tex); } elsif ($next eq '\(') { # math inline parse_math($tex,$count,$status,6,'\)'); } elsif ($next eq '\[') { # math display parse_math($tex,$count,$status,7,'\]'); } elsif ($next eq '\def') { # ignore \def... $tex->{'line'} =~ s/^([^\{]*)\{/\{/; flush_next($tex); print_style($1.' ','ignore'); parse_unit($tex,-2); } elsif (defined (my $addsuffix=$TeXfileinclude{$next})) { # include file: queue up for parsing parse_include_file($tex,$count,$status,$addsuffix); } elsif (defined ($substat=$TeXmacro{$next})) { # macro: exclude options if (defined $TeXheader{$next}) {${$count}[4]++;} gobble_macro_parms($tex,$substat,$status); } elsif (defined ($substat=$TeXmacroword{$next})) { # count macro as word (or a given number of words) ${$count}[$status]+=$substat; set_style($tex,'word'.$status); } elsif ($next =~ /^\\[^\w\_]/) { } else { gobble_option($tex); } } elsif ($next eq '$') { # math inline parse_math($tex,$count,$status,6,'$'); } elsif ($next eq '$$') { # math display (unless already in inlined math) if (! $end eq '$') {parse_math($tex,$count,$status,7,'$$');} } if (!defined $end) {return;} } } sub gobble_option { my $tex=shift @_; flush_next($tex); if ($tex->{'line'} =~ s/^(\[(\w|[,\-\s\~\.\:\;\+\?\*\_\=])*\])//) { print_style($1,'option'); return $1; } return undef; } sub parse_tc { my ($tex)=@_; set_style($tex,'tc'); if ($tex->{'next'}=~/^\%+TC:\s*(\w+)\s+([\\]*\w+)\s+([^\s\n]+)(\s+([0-9]+))?/) { my $instr=$1; my $macro=$2; my $param=$3; my $option=$5; if ($param=~/^\[([0-9,]+)\]$/) {$param=[split(',',$1)];} if (($instr eq 'macro') || ($instr eq 'exclude')) {$TeXmacro{$macro}=$param;} elsif ($instr eq 'header') {$TeXheader{$macro}=$param;$TeXmacro{$macro}=$param;} elsif ($instr eq 'macroword') {$TeXmacroword{$macro}=$param;} elsif ($instr eq 'preambleinclude') {$TeXpreamble{$macro}=$param;} elsif ($instr eq 'group') {$TeXmacro{'begin'.$macro}=$param;$TeXgroup{$macro}=$option;} elsif ($instr eq 'floatinclude') {$TeXfloatinc{$macro}=$param;} elsif ($instr eq 'fileinclude') {$TeXfileinclude{$macro}=$param;} else {print_error("Warning: Unknown TC command: ".$instr);} } elsif ($tex->{'next'}=~/^\%+TC:\s*(\w+)/) { my $instr=$1; print_error("Warning: Unknown TC command: ".$instr); } } sub parse_math { my ($tex,$count,$status,$substat,$end)=@_; my $localstyle=$status>0 ? 'mathgroup' : 'exclmath'; if ($status>0) {${$count}[$substat]++;} set_style($tex,$localstyle); parse_unit($tex,0,$end); set_style($tex,$localstyle); } sub parse_begin_end { my ($tex,$count,$status)=@_; my $localstyle=$status>0 ? 'grouping' : 'exclgroup'; flush_style($tex,$localstyle); gobble_option($tex); my $groupname; if ($tex->{'line'} =~ s/^\{\s*([^\{\}]+)\s*\*?\}[ \t\r\f]*//) { # gobble group type $groupname=$1; print_style('{'.$1.'}',$localstyle); my $next='begin'.$1; if (defined (my $substat=$TeXmacro{$next})) { gobble_macro_parms($tex,$substat); } } else { print_error("Warning: BEGIN group without type."); } # find group status (or leave unchanged) my $substat; defined ($substat=$TeXgroup{$1}) || ($substat=$status); if ($status<=0 && $status<$substat) {$substat=$status;} if (($status>0) && ($substat==-1)) { # Count float ${$count}[5]++; } if ($status>0 and $substat>3) { # count item, exclude contents ${$count}[$substat]++; $substat=0; } parse_unit($tex,$substat,'\end'); if ($tex->{'line'} =~ s/^\{\s*([^\{\}]+)\s*\}[ \t\r\f]*//) { # gobble group type flush_style($tex,$localstyle); print_style('{'.$1.'}',$localstyle); } else { print_error("Warning: END group without type while waiting to end ".$groupname."."); } } sub parse_include_file { my ($tex,$count,$status,$addsuffix)=@_; $tex->{'line'} =~ s/^\{([^\{\}\s]+)\}// || $tex->{'line'} =~ s/^\s*([^\{\}\%\\\s]+)// || return; flush_next($tex); if ($status>0) { print_style($&,'fileinclude'); my $fname=$1; if ($addsuffix && ($fname=~/^[^\.]+$/)) {$fname.='.tex';} if ($includeTeX) {push @filelist,$tex->{'filepath'}.$fname;} } else { print_style($&,'ignored'); } } sub gobble_options { while (gobble_option(@_)) {} } sub gobble_macro_modifier { my $tex=shift @_; flush_next($tex); if ($tex->{'line'} =~ s/^\*//) { print_style($1,'option'); return $1; } return undef; } sub gobble_macro_parms { my ($tex,$parm,$oldstat)=@_; my $i; if (ref($parm) eq 'ARRAY') { $i=scalar @{$parm}; } else { $i=$parm; $parm=[0,0,0,0,0,0,0,0,0]; } if ($i>0) {gobble_macro_modifier($tex);} gobble_options($tex); for (my $j=0;$j<$i;$j++) { parse_unit($tex,new_status($parm->[$j],$oldstat)); gobble_options($tex); } } sub new_status { my ($substat,$old)=@_; if (!defined $old) {return $substat;} if ($old==-3 || $substat==-3) {return -3;} if ($old==-2 || $substat==-2) {return -2;} if ($old==0 || $substat==0) {return 0;} if ($old==-9 || $substat==-9) {return -9;} if ($old>$substat) {return $old;} return $substat; } ### HTML context sub html_head { print '