package TeX::Parser;

use base HTML::Parser;
use ML::Tree;
use strict;
use FileHandle;
use Carp;

# this->{'fh'} .. currently opened file for reading
# this->{'file'}
# this->{'line'}

# this->{'iline'} .. currently buffered line, used by input and token processor
# this->{'tkline'} .. currently buffered line, used by token processor(array)
# this->{'tkstate'} .. token processor state

# this->{'reg'} .. tex registers
# this->{'cat'} .. array, category table


sub new
{
    my $class = shift;
    $class = ref( $class ) || $class;

    my $this = {};
    bless( $this, $class );
    
    $this->{'cat'} = CreateCategoryTable();
    this->{'tkline'} = [];
    this->{'tkstate'} = 'N';

}
sub croak_fl
{
    my ( $this, $tx ) = @_;

    croak( "$this->{'file'}:$this->{'line'}: $tx" );
}

# Token processor ==============================================================

# ($char, $cat) = $this->TokenGet()
# $char .. string, $cat .. category or undef for control seq
# $char undef if no token
sub TokenGet
{
    my $this = shift;
    my $l = this->{'tkline'};
    my ( $cat, $ch );
    my $ctlseq = undef;

    while(1)
    {
        if( $#{$l} < 0 )                # ask input processor for new line
        {
            $this->InputLine() or return ( undef, undef ); # no tokens on input
            @$l = unpack( 'c*', $this->{'iline'} );
            $this->{'tkstate'} = 'N';
        }

        ($ch, $cat) = $this->Token_getchardblhat();

        $cat == 0 and do { # control sequence
            $#{$l} < 0 and return '';   # empty control seq ( \ at eol)
            
            ($ch, $cat) = $this->Token_getchardblhat();
            $cat != 11 and do {
                $this->{'tkstate'} = $cat == 10 ? 'S' : 'M';
                return $ch;
            };

            while( $cat == 10 )
            {
                $#{$l} < 0 and return $ch; # physical eol
                ($ch, $cat) = $this->Token_getchardblhat();
            }

            $ctlseq = '';
        }


        defined( $ctlseq ) and do 
        {
            if( $cat == 11 ) { $ctlseq .= $ch; next }
            elsif( $ctlseq eq '' ) { $ctlseq = $ch;
            next;
            
        }
       



}

# (a) .. indirect characters (^^M, ^^f8 ... )

# ( ch, cat ) = Token_getchardblhat( $this );
sub Token_getchardblhat
{
    my ( $this ) = @_;
    my $l = this->{'tkline'};

    while(1)
    {
        $ch = shift( @$l );
        $cat = $this->{'cat'}[ $ch ];
        $cat == 7 and $l->[0] == $ch and do {
            if( $l->[1] =~ /[0-9a-f]/ and $l->[2] =~ /[0-9a-f]/ ) # hex
            {
                $l->[1] -= $l->[1] >= 'a' ? 'a' - 10 : '0';
                $l->[2] -= $l->[2] >= 'a' ? 'a' - 10 : '0';
                $l->[2] += $l->[1] * 16;
                shift @$l;
            }
            elsif( $l->[1] < 128 ) { $l->[1] += $l->[1] >= 64 ? -64 : 64; }
            else { $this->croak_fl( "Token ???" ); }
            shift @$l;
            next;
        };
        return ( $ch, $cat );
    };
}

# Input processor ==============================================================

# $line =  $this->InputLine()
# $line: text or undef
sub InputLine
{
    my $this = shift;

    $this->{'fh'} or croak "Input: no filehandle";
    $this->{'fh'}->eof() and return 0;

    $_ = $this->{'fh'}->getline();
    $this->{'line'} ++;
    chomp $_;
    s/[ ]*$//;

    $this->{'iline'} = $_ . $this->{'reg'}{'endlinechar'};
    return 1;
}

# Init section =================================================================
sub CreateCategoryTable
{
    my @cat = ();

    $cat[0..255] = 12;
    $cat['\\'] = 0;
    $cat['{'] = 1;
    $cat['}'] = 2;
    $cat["\$"] = 3;
    $cat['&'] = 4;
    $cat[13] = 5;
    $cat["\#"] = 6;
    $cat['^'] = 7;
    $cat['_'] = 8;
    $cat[0] = 9;
    $cat[' '] = 10;
    $cat['A'..'Z','a'..'z'] = 11;
    $cat['~',12] = 13;
    $cat['%'] = 14;
    $cat[127] = 15;

    return \@cat;
}
 

1;
