/[suikacvs]/markup/html/whatpm/Whatpm/HTML.pm.src
Suika

Diff of /markup/html/whatpm/Whatpm/HTML.pm.src

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.160 by wakaba, Wed Sep 10 10:27:08 2008 UTC revision 1.184 by wakaba, Mon Sep 15 09:02:27 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
# Line 354  sub parse_byte_string ($$$$;$) { Line 365  sub parse_byte_string ($$$$;$) {
365    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
366  } # parse_byte_string  } # parse_byte_string
367    
368  sub parse_byte_stream ($$$$;$) {  sub parse_byte_stream ($$$$;$$) {
369      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
370    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
371    my $charset_name = shift;    my $charset_name = shift;
372    my $byte_stream = $_[0];    my $byte_stream = $_[0];
# Line 365  sub parse_byte_stream ($$$$;$) { Line 377  sub parse_byte_stream ($$$$;$) {
377    };    };
378    $self->{parse_error} = $onerror; # updated later by parse_char_string    $self->{parse_error} = $onerror; # updated later by parse_char_string
379    
380      my $get_wrapper = $_[3] || sub ($) {
381        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
382      };
383    
384    ## HTML5 encoding sniffing algorithm    ## HTML5 encoding sniffing algorithm
385    require Message::Charset::Info;    require Message::Charset::Info;
386    my $charset;    my $charset;
# Line 381  sub parse_byte_stream ($$$$;$) { Line 397  sub parse_byte_stream ($$$$;$) {
397    
398      ## Step 1      ## Step 1
399      if (defined $charset_name) {      if (defined $charset_name) {
400        $charset = Message::Charset::Info->get_by_iana_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
401              ## TODO: Is this ok?  Transfer protocol's parameter should be
402              ## interpreted in its semantics?
403    
404        ## ISSUE: Unsupported encoding is not ignored according to the spec.        ## ISSUE: Unsupported encoding is not ignored according to the spec.
405        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
# Line 405  sub parse_byte_stream ($$$$;$) { Line 423  sub parse_byte_stream ($$$$;$) {
423    
424      ## Step 3      ## Step 3
425      if ($byte_buffer =~ /^\xFE\xFF/) {      if ($byte_buffer =~ /^\xFE\xFF/) {
426        $charset = Message::Charset::Info->get_by_iana_name ('utf-16be');        $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
427        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
428            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
429             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
430        $self->{confident} = 1;        $self->{confident} = 1;
431        last SNIFFING;        last SNIFFING;
432      } elsif ($byte_buffer =~ /^\xFF\xFE/) {      } elsif ($byte_buffer =~ /^\xFF\xFE/) {
433        $charset = Message::Charset::Info->get_by_iana_name ('utf-16le');        $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
434        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
435            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
436             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
437        $self->{confident} = 1;        $self->{confident} = 1;
438        last SNIFFING;        last SNIFFING;
439      } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {      } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
440        $charset = Message::Charset::Info->get_by_iana_name ('utf-8');        $charset = Message::Charset::Info->get_by_html_name ('utf-8');
441        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
442            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
443             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
# Line 438  sub parse_byte_stream ($$$$;$) { Line 456  sub parse_byte_stream ($$$$;$) {
456      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
457          ($byte_buffer);          ($byte_buffer);
458      if (defined $charset_name) {      if (defined $charset_name) {
459        $charset = Message::Charset::Info->get_by_iana_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
460    
461        ## ISSUE: Unsupported encoding is not ignored according to the spec.        ## ISSUE: Unsupported encoding is not ignored according to the spec.
462        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
# Line 461  sub parse_byte_stream ($$$$;$) { Line 479  sub parse_byte_stream ($$$$;$) {
479    
480      ## Step 7: default      ## Step 7: default
481      ## TODO: Make this configurable.      ## TODO: Make this configurable.
482      $charset = Message::Charset::Info->get_by_iana_name ('windows-1252');      $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
483          ## NOTE: We choose |windows-1252| here, since |utf-8| should be          ## NOTE: We choose |windows-1252| here, since |utf-8| should be
484          ## detectable in the step 6.          ## detectable in the step 6.
485      require Whatpm::Charset::DecodeHandle;      require Whatpm::Charset::DecodeHandle;
# Line 489  sub parse_byte_stream ($$$$;$) { Line 507  sub parse_byte_stream ($$$$;$) {
507                      line => 1, column => 1,                      line => 1, column => 1,
508                      layer => 'encode');                      layer => 'encode');
509    } elsif (not ($e_status &    } elsif (not ($e_status &
510                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
511      $self->{input_encoding} = $charset->get_iana_name;      $self->{input_encoding} = $charset->get_iana_name;
512      !!!parse-error (type => 'chardecode:no error',      !!!parse-error (type => 'chardecode:no error',
513                      text => $self->{input_encoding},                      text => $self->{input_encoding},
# Line 505  sub parse_byte_stream ($$$$;$) { Line 523  sub parse_byte_stream ($$$$;$) {
523      $charset_name = shift;      $charset_name = shift;
524      my $token = shift;      my $token = shift;
525    
526      $charset = Message::Charset::Info->get_by_iana_name ($charset_name);      $charset = Message::Charset::Info->get_by_html_name ($charset_name);
527      ($char_stream, $e_status) = $charset->get_decode_handle      ($char_stream, $e_status) = $charset->get_decode_handle
528          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
529           byte_buffer => \ $buffer->{buffer});           byte_buffer => \ $buffer->{buffer});
# Line 516  sub parse_byte_stream ($$$$;$) { Line 534  sub parse_byte_stream ($$$$;$) {
534        ## Step 1            ## Step 1    
535        if ($charset->{category} &        if ($charset->{category} &
536            Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {            Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
537          $charset = Message::Charset::Info->get_by_iana_name ('utf-8');          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
538          ($char_stream, $e_status) = $charset->get_decode_handle          ($char_stream, $e_status) = $charset->get_decode_handle
539              ($byte_stream,              ($byte_stream,
540               byte_buffer => \ $buffer->{buffer});               byte_buffer => \ $buffer->{buffer});
# Line 554  sub parse_byte_stream ($$$$;$) { Line 572  sub parse_byte_stream ($$$$;$) {
572    my $char_onerror = sub {    my $char_onerror = sub {
573      my (undef, $type, %opt) = @_;      my (undef, $type, %opt) = @_;
574      !!!parse-error (layer => 'encode',      !!!parse-error (layer => 'encode',
575                      %opt, type => $type,                      line => $self->{line}, column => $self->{column} + 1,
576                      line => $self->{line}, column => $self->{column} + 1);                      %opt, type => $type);
577      if ($opt{octets}) {      if ($opt{octets}) {
578        ${$opt{octets}} = "\x{FFFD}"; # relacement character        ${$opt{octets}} = "\x{FFFD}"; # relacement character
579      }      }
580    };    };
   $char_stream->onerror ($char_onerror);  
581    
582    my @args = @_; shift @args; # $s    my $wrapped_char_stream = $get_wrapper->($char_stream);
583      $wrapped_char_stream->onerror ($char_onerror);
584    
585      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
586    my $return;    my $return;
587    try {    try {
588      $return = $self->parse_char_stream ($char_stream, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
589    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
590      ## NOTE: Invoked after {change_encoding}.      ## NOTE: Invoked after {change_encoding}.
591    
# Line 577  sub parse_byte_stream ($$$$;$) { Line 597  sub parse_byte_stream ($$$$;$) {
597                        line => 1, column => 1,                        line => 1, column => 1,
598                        layer => 'encode');                        layer => 'encode');
599      } elsif (not ($e_status &      } elsif (not ($e_status &
600                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
601        $self->{input_encoding} = $charset->get_iana_name;        $self->{input_encoding} = $charset->get_iana_name;
602        !!!parse-error (type => 'chardecode:no error',        !!!parse-error (type => 'chardecode:no error',
603                        text => $self->{input_encoding},                        text => $self->{input_encoding},
# Line 588  sub parse_byte_stream ($$$$;$) { Line 608  sub parse_byte_stream ($$$$;$) {
608        $self->{input_encoding} = $charset->get_iana_name;        $self->{input_encoding} = $charset->get_iana_name;
609      }      }
610      $self->{confident} = 1;      $self->{confident} = 1;
611      $char_stream->onerror ($char_onerror);  
612      $return = $self->parse_char_stream ($char_stream, @args);      $wrapped_char_stream = $get_wrapper->($char_stream);
613        $wrapped_char_stream->onerror ($char_onerror);
614    
615        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
616    };    };
617    return $return;    return $return;
618  } # parse_byte_stream  } # parse_byte_stream
# Line 603  sub parse_byte_stream ($$$$;$) { Line 626  sub parse_byte_stream ($$$$;$) {
626  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
627  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
628    
629  sub parse_char_string ($$$;$) {  sub parse_char_string ($$$;$$) {
630      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
631    my $self = shift;    my $self = shift;
   require utf8;  
632    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $s = ref $_[0] ? $_[0] : \($_[0]);
633    open my $input, '<' . (utf8::is_utf8 ($$s) ? ':utf8' : ''), $s;    require Whatpm::Charset::DecodeHandle;
634      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
635    return $self->parse_char_stream ($input, @_[1..$#_]);    return $self->parse_char_stream ($input, @_[1..$#_]);
636  } # parse_char_string  } # parse_char_string
637  *parse_string = \&parse_char_string;  *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
638    
639  sub parse_char_stream ($$$;$) {  sub parse_char_stream ($$$;$$) {
640    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
641    my $input = $_[0];    my $input = $_[0];
642    $self->{document} = $_[1];    $self->{document} = $_[1];
# Line 623  sub parse_char_stream ($$$;$) { Line 647  sub parse_char_stream ($$$;$) {
647    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
648    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
649        if defined $self->{input_encoding};        if defined $self->{input_encoding};
650    ## TODO: |{input_encoding}| is needless?
651    
   my $i = 0;  
652    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
653    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
654    $self->{set_next_char} = sub {    $self->{column} = 0;
655      $self->{set_nc} = sub {
656      my $self = shift;      my $self = shift;
657    
658      pop @{$self->{prev_char}};      my $char = '';
659      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
660          $char = $self->{next_nc};
661      my $char;        delete $self->{next_nc};
662      if (defined $self->{next_next_char}) {        $self->{nc} = ord $char;
       $char = $self->{next_next_char};  
       delete $self->{next_next_char};  
663      } else {      } else {
664        $char = $input->getc;        $self->{char_buffer} = '';
665          $self->{char_buffer_pos} = 0;
666    
667          my $count = $input->manakai_read_until
668             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
669          if ($count) {
670            $self->{line_prev} = $self->{line};
671            $self->{column_prev} = $self->{column};
672            $self->{column}++;
673            $self->{nc}
674                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
675            return;
676          }
677    
678          if ($input->read ($char, 1)) {
679            $self->{nc} = ord $char;
680          } else {
681            $self->{nc} = -1;
682            return;
683          }
684      }      }
     $self->{next_char} = -1 and return unless defined $char;  
     $self->{next_char} = ord $char;  
685    
686      ($self->{line_prev}, $self->{column_prev})      ($self->{line_prev}, $self->{column_prev})
687          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
688      $self->{column}++;      $self->{column}++;
689            
690      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
691        !!!cp ('j1');        !!!cp ('j1');
692        $self->{line}++;        $self->{line}++;
693        $self->{column} = 0;        $self->{column} = 0;
694      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
695        !!!cp ('j2');        !!!cp ('j2');
696        my $next = $input->getc;  ## TODO: support for abort/streaming
697        if (defined $next and $next ne "\x0A") {        my $next = '';
698          $self->{next_next_char} = $next;        if ($input->read ($next, 1) and $next ne "\x0A") {
699            $self->{next_nc} = $next;
700        }        }
701        $self->{next_char} = 0x000A; # LF # MUST        $self->{nc} = 0x000A; # LF # MUST
702        $self->{line}++;        $self->{line}++;
703        $self->{column} = 0;        $self->{column} = 0;
704      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{nc} == 0x0000) { # NULL
       !!!cp ('j3');  
       $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
     } elsif ($self->{next_char} == 0x0000) { # NULL  
705        !!!cp ('j4');        !!!cp ('j4');
706        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
707        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
708      } elsif ($self->{next_char} <= 0x0008 or      }
709               (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or    };
710               (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or  
711               (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or    $self->{read_until} = sub {
712               (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or      #my ($scalar, $specials_range, $offset) = @_;
713               {      return 0 if defined $self->{next_nc};
714                0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
715                0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,      my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
716                0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,      my $offset = $_[2] || 0;
717                0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
718                0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,      if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
719                0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,        pos ($self->{char_buffer}) = $self->{char_buffer_pos};
720                0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,        if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
721                0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,          substr ($_[0], $offset)
722                0x10FFFE => 1, 0x10FFFF => 1,              = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
723               }->{$self->{next_char}}) {          my $count = $+[0] - $-[0];
724        !!!cp ('j5');          if ($count) {
725        if ($self->{next_char} < 0x10000) {            $self->{column} += $count;
726          !!!parse-error (type => 'control char',            $self->{char_buffer_pos} += $count;
727                          text => (sprintf 'U+%04X', $self->{next_char}));            $self->{line_prev} = $self->{line};
728              $self->{column_prev} = $self->{column} - 1;
729              $self->{nc} = -1;
730            }
731            return $count;
732        } else {        } else {
733          !!!parse-error (type => 'control char',          return 0;
                         text => (sprintf 'U-%08X', $self->{next_char}));  
734        }        }
735        } else {
736          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
737          if ($count) {
738            $self->{column} += $count;
739            $self->{line_prev} = $self->{line};
740            $self->{column_prev} = $self->{column} - 1;
741            $self->{nc} = -1;
742          }
743          return $count;
744      }      }
745    };    }; # $self->{read_until}
   $self->{prev_char} = [-1, -1, -1];  
   $self->{next_char} = -1;  
746    
747    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
748      my (%opt) = @_;      my (%opt) = @_;
# Line 706  sub parse_char_stream ($$$;$) { Line 754  sub parse_char_stream ($$$;$) {
754      $onerror->(line => $self->{line}, column => $self->{column}, @_);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
755    };    };
756    
757      my $char_onerror = sub {
758        my (undef, $type, %opt) = @_;
759        !!!parse-error (layer => 'encode',
760                        line => $self->{line}, column => $self->{column} + 1,
761                        %opt, type => $type);
762      }; # $char_onerror
763    
764      if ($_[3]) {
765        $input = $_[3]->($input);
766        $input->onerror ($char_onerror);
767      } else {
768        $input->onerror ($char_onerror) unless defined $input->onerror;
769      }
770    
771    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
772    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
773    $self->_construct_tree;    $self->_construct_tree;
# Line 725  sub new ($) { Line 787  sub new ($) {
787                info => 'i',                info => 'i',
788                uncertain => 'u'},                uncertain => 'u'},
789    }, $class;    }, $class;
790    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
791      $self->{next_char} = -1;      $self->{nc} = -1;
792    };    };
793    $self->{parse_error} = sub {    $self->{parse_error} = sub {
794      #      #
# Line 753  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 815  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
815  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
816    
817  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
818  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
819  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
820  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
821  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 764  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 826  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
826  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
827  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
828  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
829  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
830  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
831  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
832  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 787  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT Line 849  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT
849  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
850  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
851  sub SELF_CLOSING_START_TAG_STATE () { 34 }  sub SELF_CLOSING_START_TAG_STATE () { 34 }
852  sub CDATA_BLOCK_STATE () { 35 }  sub CDATA_SECTION_STATE () { 35 }
853    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
854    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
855    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
856    sub CDATA_PCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
857    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
858    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
859    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
860    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
861    ## NOTE: "Entity data state", "entity in attribute value state", and
862    ## "consume a character reference" algorithm are jointly implemented
863    ## using the following six states:
864    sub ENTITY_STATE () { 44 }
865    sub ENTITY_HASH_STATE () { 45 }
866    sub NCR_NUM_STATE () { 46 }
867    sub HEXREF_X_STATE () { 47 }
868    sub HEXREF_HEX_STATE () { 48 }
869    sub ENTITY_NAME_STATE () { 49 }
870    
871  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
872  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 840  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 919  sub IN_COLUMN_GROUP_IM () { 0b10 }
919  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
920    my $self = shift;    my $self = shift;
921    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
922      #$self->{s_kwd}; # state keyword - initialized when used
923      #$self->{entity__value}; # initialized when used
924      #$self->{entity__match}; # initialized when used
925    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
926    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
927    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
928    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
929    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
930    delete $self->{self_closing};    delete $self->{self_closing};
931    $self->{char} = [];    $self->{char_buffer} = '';
932    # $self->{next_char}    $self->{char_buffer_pos} = 0;
933      $self->{nc} = -1; # next input character
934      #$self->{next_nc}
935    !!!next-input-character;    !!!next-input-character;
936    $self->{token} = [];    $self->{token} = [];
937    # $self->{escape}    # $self->{escape}
# Line 858  sub _initialize_tokenizer ($) { Line 942  sub _initialize_tokenizer ($) {
942  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
943  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
944  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
945  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
946  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
947  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
948  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
949  ##        ->{name}  ##        ->{name}
# Line 878  sub _initialize_tokenizer ($) { Line 962  sub _initialize_tokenizer ($) {
962  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
963  ## and removed from the list.  ## and removed from the list.
964    
965  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
966  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
 ## contains some requirements that are not detected by the  
 ## parsing algorithm:  
 ## - Some requirements on character encoding declarations. ## TODO  
 ## - "Elements MUST NOT contain content that their content model disallows."  
 ##   ... Some are parse error, some are not (will be reported by c.c.).  
 ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO  
 ## - Text (in elements, attributes, and comments) SHOULD NOT contain  
 ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)  
   
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
967    
968  sub _get_next_token ($) {  sub _get_next_token ($) {
969    my $self = shift;    my $self = shift;
970    
971    if ($self->{self_closing}) {    if ($self->{self_closing}) {
972      !!!parse-error (type => 'nestc', token => $self->{current_token});      !!!parse-error (type => 'nestc', token => $self->{ct});
973      ## NOTE: The |self_closing| flag is only set by start tag token.      ## NOTE: The |self_closing| flag is only set by start tag token.
974      ## In addition, when a start tag token is emitted, it is always set to      ## In addition, when a start tag token is emitted, it is always set to
975      ## |current_token|.      ## |ct|.
976      delete $self->{self_closing};      delete $self->{self_closing};
977    }    }
978    
# Line 911  sub _get_next_token ($) { Line 983  sub _get_next_token ($) {
983    
984    A: {    A: {
985      if ($self->{state} == DATA_STATE) {      if ($self->{state} == DATA_STATE) {
986        if ($self->{next_char} == 0x0026) { # &        if ($self->{nc} == 0x0026) { # &
987            delete $self->{s_kwd};
988          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
989              not $self->{escape}) {              not $self->{escape}) {
990            !!!cp (1);            !!!cp (1);
991            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
992              ## "entity data state".  In this implementation, the tokenizer
993              ## is switched to the |ENTITY_STATE|, which is an implementation
994              ## of the "consume a character reference" algorithm.
995              $self->{entity_add} = -1;
996              $self->{prev_state} = DATA_STATE;
997              $self->{state} = ENTITY_STATE;
998            !!!next-input-character;            !!!next-input-character;
999            redo A;            redo A;
1000          } else {          } else {
1001            !!!cp (2);            !!!cp (2);
1002            #            #
1003          }          }
1004        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1005          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1006            unless ($self->{escape}) {            if (defined $self->{s_kwd}) {
1007              if ($self->{prev_char}->[0] == 0x002D and # -              !!!cp (2.1);
1008                  $self->{prev_char}->[1] == 0x0021 and # !              $self->{s_kwd} .= '-';
1009                  $self->{prev_char}->[2] == 0x003C) { # <            } else {
1010                !!!cp (3);              !!!cp (2.2);
1011                $self->{escape} = 1;              $self->{s_kwd} = '-';
1012              } else {            }
1013                !!!cp (4);  
1014              }            if ($self->{s_kwd} eq '<!--') {
1015                !!!cp (3);
1016                $self->{escape} = 1; # unless $self->{escape};
1017                $self->{s_kwd} = '--';
1018                #
1019              } elsif ($self->{s_kwd} eq '---') {
1020                !!!cp (4);
1021                $self->{s_kwd} = '--';
1022                #
1023            } else {            } else {
1024              !!!cp (5);              !!!cp (5);
1025                #
1026            }            }
1027          }          }
1028                    
1029          #          #
1030        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1031            if (defined $self->{s_kwd}) {
1032              !!!cp (5.1);
1033              $self->{s_kwd} .= '!';
1034              #
1035            } else {
1036              !!!cp (5.2);
1037              #
1038            }
1039            #
1040          } elsif ($self->{nc} == 0x003C) { # <
1041            delete $self->{s_kwd};
1042          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1043              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1044               not $self->{escape})) {               not $self->{escape})) {
# Line 951  sub _get_next_token ($) { Line 1050  sub _get_next_token ($) {
1050            !!!cp (7);            !!!cp (7);
1051            #            #
1052          }          }
1053        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1054          if ($self->{escape} and          if ($self->{escape} and
1055              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1056            if ($self->{prev_char}->[0] == 0x002D and # -            if (defined $self->{s_kwd} and $self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1057              !!!cp (8);              !!!cp (8);
1058              delete $self->{escape};              delete $self->{escape};
1059            } else {            } else {
# Line 965  sub _get_next_token ($) { Line 1063  sub _get_next_token ($) {
1063            !!!cp (10);            !!!cp (10);
1064          }          }
1065                    
1066            delete $self->{s_kwd};
1067          #          #
1068        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1069          !!!cp (11);          !!!cp (11);
1070            delete $self->{s_kwd};
1071          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1072                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1073          last A; ## TODO: ok?          last A; ## TODO: ok?
1074        } else {        } else {
1075          !!!cp (12);          !!!cp (12);
1076            delete $self->{s_kwd};
1077            #
1078        }        }
1079    
1080        # Anything else        # Anything else
1081        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1082                     data => chr $self->{next_char},                     data => chr $self->{nc},
1083                     line => $self->{line}, column => $self->{column},                     line => $self->{line}, column => $self->{column},
1084                    };                    };
1085          if ($self->{read_until}->($token->{data}, q[-!<>&],
1086                                    length $token->{data})) {
1087            delete $self->{s_kwd};
1088          }
1089    
1090        ## Stay in the data state        ## Stay in the data state
1091        !!!next-input-character;        !!!next-input-character;
   
1092        !!!emit ($token);        !!!emit ($token);
   
       redo A;  
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev});  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
   
       unless (defined $token) {  
         !!!cp (13);  
         !!!emit ({type => CHARACTER_TOKEN, data => '&',  
                   line => $l, column => $c,  
                  });  
       } else {  
         !!!cp (14);  
         !!!emit ($token);  
       }  
   
1093        redo A;        redo A;
1094      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1095        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1096          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1097            !!!cp (15);            !!!cp (15);
1098            !!!next-input-character;            !!!next-input-character;
1099            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1100            redo A;            redo A;
1101            } elsif ($self->{nc} == 0x0021) { # !
1102              !!!cp (15.1);
1103              $self->{s_kwd} = '<' unless $self->{escape};
1104              #
1105          } else {          } else {
1106            !!!cp (16);            !!!cp (16);
1107            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
1108          }          }
1109    
1110            ## reconsume
1111            $self->{state} = DATA_STATE;
1112            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1113                      line => $self->{line_prev},
1114                      column => $self->{column_prev},
1115                     });
1116            redo A;
1117        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1118          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1119            !!!cp (17);            !!!cp (17);
1120            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1121            !!!next-input-character;            !!!next-input-character;
1122            redo A;            redo A;
1123          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1124            !!!cp (18);            !!!cp (18);
1125            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1126            !!!next-input-character;            !!!next-input-character;
1127            redo A;            redo A;
1128          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1129                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1130            !!!cp (19);            !!!cp (19);
1131            $self->{current_token}            $self->{ct}
1132              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1133                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1134                 line => $self->{line_prev},                 line => $self->{line_prev},
1135                 column => $self->{column_prev}};                 column => $self->{column_prev}};
1136            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1137            !!!next-input-character;            !!!next-input-character;
1138            redo A;            redo A;
1139          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1140                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1141            !!!cp (20);            !!!cp (20);
1142            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1143                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1144                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1145                                      column => $self->{column_prev}};                                      column => $self->{column_prev}};
1146            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1147            !!!next-input-character;            !!!next-input-character;
1148            redo A;            redo A;
1149          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1150            !!!cp (21);            !!!cp (21);
1151            !!!parse-error (type => 'empty start tag',            !!!parse-error (type => 'empty start tag',
1152                            line => $self->{line_prev},                            line => $self->{line_prev},
# Line 1071  sub _get_next_token ($) { Line 1160  sub _get_next_token ($) {
1160                     });                     });
1161    
1162            redo A;            redo A;
1163          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1164            !!!cp (22);            !!!cp (22);
1165            !!!parse-error (type => 'pio',            !!!parse-error (type => 'pio',
1166                            line => $self->{line_prev},                            line => $self->{line_prev},
1167                            column => $self->{column_prev});                            column => $self->{column_prev});
1168            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1169            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1170                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1171                                      column => $self->{column_prev},                                      column => $self->{column_prev},
1172                                     };                                     };
1173            ## $self->{next_char} is intentionally left as is            ## $self->{nc} is intentionally left as is
1174            redo A;            redo A;
1175          } else {          } else {
1176            !!!cp (23);            !!!cp (23);
# Line 1102  sub _get_next_token ($) { Line 1191  sub _get_next_token ($) {
1191          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1192        }        }
1193      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1194          ## NOTE: The "close tag open state" in the spec is implemented as
1195          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_PCDATA_CLOSE_TAG_STATE|.
1196    
1197        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1198        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1199          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1200              $self->{state} = CDATA_PCDATA_CLOSE_TAG_STATE;
1201            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1202            my @next_char;            ## Reconsume.
1203            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {            redo A;
             push @next_char, $self->{next_char};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_char} == $c or $self->{next_char} == $C) {  
               !!!cp (24);  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               !!!cp (25);  
               $self->{next_char} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
   
               !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                         line => $l, column => $c,  
                        });  
     
               redo A;  
             }  
           }  
           push @next_char, $self->{next_char};  
         
           unless ($self->{next_char} == 0x0009 or # HT  
                   $self->{next_char} == 0x000A or # LF  
                   $self->{next_char} == 0x000B or # VT  
                   $self->{next_char} == 0x000C or # FF  
                   $self->{next_char} == 0x0020 or # SP  
                   $self->{next_char} == 0x003E or # >  
                   $self->{next_char} == 0x002F or # /  
                   $self->{next_char} == -1) {  
             !!!cp (26);  
             $self->{next_char} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                       line => $l, column => $c,  
                      });  
             redo A;  
           } else {  
             !!!cp (27);  
             $self->{next_char} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1204          } else {          } else {
1205            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1206              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1207            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1208            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1209              ## Reconsume.
1210            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1211                      line => $l, column => $c,                      line => $l, column => $c,
1212                     });                     });
1213            redo A;            redo A;
1214          }          }
1215        }        }
1216          
1217        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1218            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1219          !!!cp (29);          !!!cp (29);
1220          $self->{current_token}          $self->{ct}
1221              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1222                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1223                 line => $l, column => $c};                 line => $l, column => $c};
1224          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1225          !!!next-input-character;          !!!next-input-character;
1226          redo A;          redo A;
1227        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1228                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1229          !!!cp (30);          !!!cp (30);
1230          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1231                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1232                                    line => $l, column => $c};                                    line => $l, column => $c};
1233          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1234          !!!next-input-character;          !!!next-input-character;
1235          redo A;          redo A;
1236        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1237          !!!cp (31);          !!!cp (31);
1238          !!!parse-error (type => 'empty end tag',          !!!parse-error (type => 'empty end tag',
1239                          line => $self->{line_prev}, ## "<" in "</>"                          line => $self->{line_prev}, ## "<" in "</>"
# Line 1192  sub _get_next_token ($) { Line 1241  sub _get_next_token ($) {
1241          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1242          !!!next-input-character;          !!!next-input-character;
1243          redo A;          redo A;
1244        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1245          !!!cp (32);          !!!cp (32);
1246          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1247          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1207  sub _get_next_token ($) { Line 1256  sub _get_next_token ($) {
1256          !!!cp (33);          !!!cp (33);
1257          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1258          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1259          $self->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1260                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1261                                    column => $self->{column_prev} - 1,                                    column => $self->{column_prev} - 1,
1262                                   };                                   };
1263          ## $self->{next_char} is intentionally left as is          ## NOTE: $self->{nc} is intentionally left as is.
1264          redo A;          ## Although the "anything else" case of the spec not explicitly
1265            ## states that the next input character is to be reconsumed,
1266            ## it will be included to the |data| of the comment token
1267            ## generated from the bogus end tag, as defined in the
1268            ## "bogus comment state" entry.
1269            redo A;
1270          }
1271        } elsif ($self->{state} == CDATA_PCDATA_CLOSE_TAG_STATE) {
1272          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1273          if (length $ch) {
1274            my $CH = $ch;
1275            $ch =~ tr/a-z/A-Z/;
1276            my $nch = chr $self->{nc};
1277            if ($nch eq $ch or $nch eq $CH) {
1278              !!!cp (24);
1279              ## Stay in the state.
1280              $self->{s_kwd} .= $nch;
1281              !!!next-input-character;
1282              redo A;
1283            } else {
1284              !!!cp (25);
1285              $self->{state} = DATA_STATE;
1286              ## Reconsume.
1287              !!!emit ({type => CHARACTER_TOKEN,
1288                        data => '</' . $self->{s_kwd},
1289                        line => $self->{line_prev},
1290                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1291                       });
1292              redo A;
1293            }
1294          } else { # after "<{tag-name}"
1295            unless ({
1296                     0x0009 => 1, # HT
1297                     0x000A => 1, # LF
1298                     0x000B => 1, # VT
1299                     0x000C => 1, # FF
1300                     0x0020 => 1, # SP
1301                     0x003E => 1, # >
1302                     0x002F => 1, # /
1303                     -1 => 1, # EOF
1304                    }->{$self->{nc}}) {
1305              !!!cp (26);
1306              ## Reconsume.
1307              $self->{state} = DATA_STATE;
1308              !!!emit ({type => CHARACTER_TOKEN,
1309                        data => '</' . $self->{s_kwd},
1310                        line => $self->{line_prev},
1311                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1312                       });
1313              redo A;
1314            } else {
1315              !!!cp (27);
1316              $self->{ct}
1317                  = {type => END_TAG_TOKEN,
1318                     tag_name => $self->{last_stag_name},
1319                     line => $self->{line_prev},
1320                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1321              $self->{state} = TAG_NAME_STATE;
1322              ## Reconsume.
1323              redo A;
1324            }
1325        }        }
1326      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1327        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
1328            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
1329            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
1330            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
1331            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
1332          !!!cp (34);          !!!cp (34);
1333          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1334          !!!next-input-character;          !!!next-input-character;
1335          redo A;          redo A;
1336        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1337          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1338            !!!cp (35);            !!!cp (35);
1339            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1340          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1341            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1342            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1343            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1344            #  !!! cp (36);            #  !!! cp (36);
1345            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1238  sub _get_next_token ($) { Line 1347  sub _get_next_token ($) {
1347              !!!cp (37);              !!!cp (37);
1348            #}            #}
1349          } else {          } else {
1350            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1351          }          }
1352          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1353          !!!next-input-character;          !!!next-input-character;
1354    
1355          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1356    
1357          redo A;          redo A;
1358        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1359                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1360          !!!cp (38);          !!!cp (38);
1361          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1362            # start tag or end tag            # start tag or end tag
1363          ## Stay in this state          ## Stay in this state
1364          !!!next-input-character;          !!!next-input-character;
1365          redo A;          redo A;
1366        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1367          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1368          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1369            !!!cp (39);            !!!cp (39);
1370            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1371          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1372            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1373            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1374            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1375            #  !!! cp (40);            #  !!! cp (40);
1376            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1269  sub _get_next_token ($) { Line 1378  sub _get_next_token ($) {
1378              !!!cp (41);              !!!cp (41);
1379            #}            #}
1380          } else {          } else {
1381            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1382          }          }
1383          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1384          # reconsume          # reconsume
1385    
1386          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1387    
1388          redo A;          redo A;
1389        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1390          !!!cp (42);          !!!cp (42);
1391          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1392          !!!next-input-character;          !!!next-input-character;
1393          redo A;          redo A;
1394        } else {        } else {
1395          !!!cp (44);          !!!cp (44);
1396          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1397            # start tag or end tag            # start tag or end tag
1398          ## Stay in the state          ## Stay in the state
1399          !!!next-input-character;          !!!next-input-character;
1400          redo A;          redo A;
1401        }        }
1402      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1403        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
1404            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
1405            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
1406            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
1407            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
1408          !!!cp (45);          !!!cp (45);
1409          ## Stay in the state          ## Stay in the state
1410          !!!next-input-character;          !!!next-input-character;
1411          redo A;          redo A;
1412        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1413          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1414            !!!cp (46);            !!!cp (46);
1415            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1416          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1417            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1418            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1419              !!!cp (47);              !!!cp (47);
1420              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1421            } else {            } else {
1422              !!!cp (48);              !!!cp (48);
1423            }            }
1424          } else {          } else {
1425            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1426          }          }
1427          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1428          !!!next-input-character;          !!!next-input-character;
1429    
1430          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1431    
1432          redo A;          redo A;
1433        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1434                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1435          !!!cp (49);          !!!cp (49);
1436          $self->{current_attribute}          $self->{ca}
1437              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1438                 value => '',                 value => '',
1439                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1440          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1441          !!!next-input-character;          !!!next-input-character;
1442          redo A;          redo A;
1443        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1444          !!!cp (50);          !!!cp (50);
1445          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1446          !!!next-input-character;          !!!next-input-character;
1447          redo A;          redo A;
1448        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1449          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1450          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1451            !!!cp (52);            !!!cp (52);
1452            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1453          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1454            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1455            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1456              !!!cp (53);              !!!cp (53);
1457              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1458            } else {            } else {
1459              !!!cp (54);              !!!cp (54);
1460            }            }
1461          } else {          } else {
1462            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1463          }          }
1464          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1465          # reconsume          # reconsume
1466    
1467          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1468    
1469          redo A;          redo A;
1470        } else {        } else {
# Line 1363  sub _get_next_token ($) { Line 1472  sub _get_next_token ($) {
1472               0x0022 => 1, # "               0x0022 => 1, # "
1473               0x0027 => 1, # '               0x0027 => 1, # '
1474               0x003D => 1, # =               0x003D => 1, # =
1475              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1476            !!!cp (55);            !!!cp (55);
1477            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1478          } else {          } else {
1479            !!!cp (56);            !!!cp (56);
1480          }          }
1481          $self->{current_attribute}          $self->{ca}
1482              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1483                 value => '',                 value => '',
1484                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1485          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1379  sub _get_next_token ($) { Line 1488  sub _get_next_token ($) {
1488        }        }
1489      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1490        my $before_leave = sub {        my $before_leave = sub {
1491          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1492              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1493            !!!cp (57);            !!!cp (57);
1494            !!!parse-error (type => 'duplicate attribute', text => $self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1495            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1496          } else {          } else {
1497            !!!cp (58);            !!!cp (58);
1498            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1499              = $self->{current_attribute};              = $self->{ca};
1500          }          }
1501        }; # $before_leave        }; # $before_leave
1502    
1503        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
1504            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
1505            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
1506            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
1507            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
1508          !!!cp (59);          !!!cp (59);
1509          $before_leave->();          $before_leave->();
1510          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1514          !!!cp (60);          !!!cp (60);
1515          $before_leave->();          $before_leave->();
1516          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1517          !!!next-input-character;          !!!next-input-character;
1518          redo A;          redo A;
1519        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1520          $before_leave->();          $before_leave->();
1521          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1522            !!!cp (61);            !!!cp (61);
1523            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1524          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1525            !!!cp (62);            !!!cp (62);
1526            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1527            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1528              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1529            }            }
1530          } else {          } else {
1531            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1532          }          }
1533          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1534          !!!next-input-character;          !!!next-input-character;
1535    
1536          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1537    
1538          redo A;          redo A;
1539        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1540                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1541          !!!cp (63);          !!!cp (63);
1542          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1543          ## Stay in the state          ## Stay in the state
1544          !!!next-input-character;          !!!next-input-character;
1545          redo A;          redo A;
1546        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1547          !!!cp (64);          !!!cp (64);
1548          $before_leave->();          $before_leave->();
1549          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1550          !!!next-input-character;          !!!next-input-character;
1551          redo A;          redo A;
1552        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1553          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1554          $before_leave->();          $before_leave->();
1555          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1556            !!!cp (66);            !!!cp (66);
1557            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1558          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1559            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1560            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1561              !!!cp (67);              !!!cp (67);
1562              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1563            } else {            } else {
# Line 1456  sub _get_next_token ($) { Line 1565  sub _get_next_token ($) {
1565              !!!cp (68);              !!!cp (68);
1566            }            }
1567          } else {          } else {
1568            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1569          }          }
1570          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1571          # reconsume          # reconsume
1572    
1573          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1574    
1575          redo A;          redo A;
1576        } else {        } else {
1577          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1578              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1579            !!!cp (69);            !!!cp (69);
1580            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1581          } else {          } else {
1582            !!!cp (70);            !!!cp (70);
1583          }          }
1584          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1585          ## Stay in the state          ## Stay in the state
1586          !!!next-input-character;          !!!next-input-character;
1587          redo A;          redo A;
1588        }        }
1589      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1590        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
1591            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
1592            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
1593            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
1594            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
1595          !!!cp (71);          !!!cp (71);
1596          ## Stay in the state          ## Stay in the state
1597          !!!next-input-character;          !!!next-input-character;
1598          redo A;          redo A;
1599        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1600          !!!cp (72);          !!!cp (72);
1601          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1602          !!!next-input-character;          !!!next-input-character;
1603          redo A;          redo A;
1604        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1605          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1606            !!!cp (73);            !!!cp (73);
1607            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1608          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1609            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1610            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1611              !!!cp (74);              !!!cp (74);
1612              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1613            } else {            } else {
# Line 1506  sub _get_next_token ($) { Line 1615  sub _get_next_token ($) {
1615              !!!cp (75);              !!!cp (75);
1616            }            }
1617          } else {          } else {
1618            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1619          }          }
1620          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1621          !!!next-input-character;          !!!next-input-character;
1622    
1623          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1624    
1625          redo A;          redo A;
1626        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1627                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1628          !!!cp (76);          !!!cp (76);
1629          $self->{current_attribute}          $self->{ca}
1630              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1631                 value => '',                 value => '',
1632                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1633          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1634          !!!next-input-character;          !!!next-input-character;
1635          redo A;          redo A;
1636        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1637          !!!cp (77);          !!!cp (77);
1638          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1639          !!!next-input-character;          !!!next-input-character;
1640          redo A;          redo A;
1641        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1642          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1643          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1644            !!!cp (79);            !!!cp (79);
1645            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1646          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1647            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1648            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1649              !!!cp (80);              !!!cp (80);
1650              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1651            } else {            } else {
# Line 1544  sub _get_next_token ($) { Line 1653  sub _get_next_token ($) {
1653              !!!cp (81);              !!!cp (81);
1654            }            }
1655          } else {          } else {
1656            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1657          }          }
1658          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1659          # reconsume          # reconsume
1660    
1661          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1662    
1663          redo A;          redo A;
1664        } else {        } else {
1665          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1666              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1667            !!!cp (78);            !!!cp (78);
1668            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1669          } else {          } else {
1670            !!!cp (82);            !!!cp (82);
1671          }          }
1672          $self->{current_attribute}          $self->{ca}
1673              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1674                 value => '',                 value => '',
1675                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1676          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1569  sub _get_next_token ($) { Line 1678  sub _get_next_token ($) {
1678          redo A;                  redo A;        
1679        }        }
1680      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1681        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
1682            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
1683            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
1684            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
1685            $self->{next_char} == 0x0020) { # SP                  $self->{nc} == 0x0020) { # SP      
1686          !!!cp (83);          !!!cp (83);
1687          ## Stay in the state          ## Stay in the state
1688          !!!next-input-character;          !!!next-input-character;
1689          redo A;          redo A;
1690        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1691          !!!cp (84);          !!!cp (84);
1692          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1693          !!!next-input-character;          !!!next-input-character;
1694          redo A;          redo A;
1695        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1696          !!!cp (85);          !!!cp (85);
1697          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1698          ## reconsume          ## reconsume
1699          redo A;          redo A;
1700        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1701          !!!cp (86);          !!!cp (86);
1702          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1703          !!!next-input-character;          !!!next-input-character;
1704          redo A;          redo A;
1705        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1706          !!!parse-error (type => 'empty unquoted attribute value');          !!!parse-error (type => 'empty unquoted attribute value');
1707          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1708            !!!cp (87);            !!!cp (87);
1709            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1710          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1711            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1712            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1713              !!!cp (88);              !!!cp (88);
1714              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1715            } else {            } else {
# Line 1608  sub _get_next_token ($) { Line 1717  sub _get_next_token ($) {
1717              !!!cp (89);              !!!cp (89);
1718            }            }
1719          } else {          } else {
1720            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1721          }          }
1722          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1723          !!!next-input-character;          !!!next-input-character;
1724    
1725          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1726    
1727          redo A;          redo A;
1728        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1729          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1730          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1731            !!!cp (90);            !!!cp (90);
1732            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1733          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1734            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1735            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1736              !!!cp (91);              !!!cp (91);
1737              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1738            } else {            } else {
# Line 1631  sub _get_next_token ($) { Line 1740  sub _get_next_token ($) {
1740              !!!cp (92);              !!!cp (92);
1741            }            }
1742          } else {          } else {
1743            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1744          }          }
1745          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1746          ## reconsume          ## reconsume
1747    
1748          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1749    
1750          redo A;          redo A;
1751        } else {        } else {
1752          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1753            !!!cp (93);            !!!cp (93);
1754            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1755          } else {          } else {
1756            !!!cp (94);            !!!cp (94);
1757          }          }
1758          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1759          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1760          !!!next-input-character;          !!!next-input-character;
1761          redo A;          redo A;
1762        }        }
1763      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1764        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1765          !!!cp (95);          !!!cp (95);
1766          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1767          !!!next-input-character;          !!!next-input-character;
1768          redo A;          redo A;
1769        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1770          !!!cp (96);          !!!cp (96);
1771          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1772          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1773            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1774            ## implementation of the "consume a character reference" algorithm.
1775            $self->{prev_state} = $self->{state};
1776            $self->{entity_add} = 0x0022; # "
1777            $self->{state} = ENTITY_STATE;
1778          !!!next-input-character;          !!!next-input-character;
1779          redo A;          redo A;
1780        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1781          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1782          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1783            !!!cp (97);            !!!cp (97);
1784            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1785          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1786            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1787            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1788              !!!cp (98);              !!!cp (98);
1789              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1790            } else {            } else {
# Line 1678  sub _get_next_token ($) { Line 1792  sub _get_next_token ($) {
1792              !!!cp (99);              !!!cp (99);
1793            }            }
1794          } else {          } else {
1795            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1796          }          }
1797          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1798          ## reconsume          ## reconsume
1799    
1800          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1801    
1802          redo A;          redo A;
1803        } else {        } else {
1804          !!!cp (100);          !!!cp (100);
1805          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1806            $self->{read_until}->($self->{ca}->{value},
1807                                  q["&],
1808                                  length $self->{ca}->{value});
1809    
1810          ## Stay in the state          ## Stay in the state
1811          !!!next-input-character;          !!!next-input-character;
1812          redo A;          redo A;
1813        }        }
1814      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1815        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1816          !!!cp (101);          !!!cp (101);
1817          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1821          !!!cp (102);          !!!cp (102);
1822          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1823          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1824            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1825            ## implementation of the "consume a character reference" algorithm.
1826            $self->{entity_add} = 0x0027; # '
1827            $self->{prev_state} = $self->{state};
1828            $self->{state} = ENTITY_STATE;
1829          !!!next-input-character;          !!!next-input-character;
1830          redo A;          redo A;
1831        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1832          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1833          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1834            !!!cp (103);            !!!cp (103);
1835            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1836          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1837            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1838            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1839              !!!cp (104);              !!!cp (104);
1840              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1841            } else {            } else {
# Line 1720  sub _get_next_token ($) { Line 1843  sub _get_next_token ($) {
1843              !!!cp (105);              !!!cp (105);
1844            }            }
1845          } else {          } else {
1846            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1847          }          }
1848          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1849          ## reconsume          ## reconsume
1850    
1851          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1852    
1853          redo A;          redo A;
1854        } else {        } else {
1855          !!!cp (106);          !!!cp (106);
1856          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1857            $self->{read_until}->($self->{ca}->{value},
1858                                  q['&],
1859                                  length $self->{ca}->{value});
1860    
1861          ## Stay in the state          ## Stay in the state
1862          !!!next-input-character;          !!!next-input-character;
1863          redo A;          redo A;
1864        }        }
1865      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1866        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
1867            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
1868            $self->{next_char} == 0x000B or # HT            $self->{nc} == 0x000B or # HT
1869            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
1870            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
1871          !!!cp (107);          !!!cp (107);
1872          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1873          !!!next-input-character;          !!!next-input-character;
1874          redo A;          redo A;
1875        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1876          !!!cp (108);          !!!cp (108);
1877          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1878          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1879            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1880            ## implementation of the "consume a character reference" algorithm.
1881            $self->{entity_add} = -1;
1882            $self->{prev_state} = $self->{state};
1883            $self->{state} = ENTITY_STATE;
1884          !!!next-input-character;          !!!next-input-character;
1885          redo A;          redo A;
1886        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1887          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1888            !!!cp (109);            !!!cp (109);
1889            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1890          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1891            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1892            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1893              !!!cp (110);              !!!cp (110);
1894              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1895            } else {            } else {
# Line 1765  sub _get_next_token ($) { Line 1897  sub _get_next_token ($) {
1897              !!!cp (111);              !!!cp (111);
1898            }            }
1899          } else {          } else {
1900            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1901          }          }
1902          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1903          !!!next-input-character;          !!!next-input-character;
1904    
1905          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1906    
1907          redo A;          redo A;
1908        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1909          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1910          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1911            !!!cp (112);            !!!cp (112);
1912            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1913          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1914            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1915            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1916              !!!cp (113);              !!!cp (113);
1917              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1918            } else {            } else {
# Line 1788  sub _get_next_token ($) { Line 1920  sub _get_next_token ($) {
1920              !!!cp (114);              !!!cp (114);
1921            }            }
1922          } else {          } else {
1923            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1924          }          }
1925          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1926          ## reconsume          ## reconsume
1927    
1928          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1929    
1930          redo A;          redo A;
1931        } else {        } else {
# Line 1801  sub _get_next_token ($) { Line 1933  sub _get_next_token ($) {
1933               0x0022 => 1, # "               0x0022 => 1, # "
1934               0x0027 => 1, # '               0x0027 => 1, # '
1935               0x003D => 1, # =               0x003D => 1, # =
1936              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1937            !!!cp (115);            !!!cp (115);
1938            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1939          } else {          } else {
1940            !!!cp (116);            !!!cp (116);
1941          }          }
1942          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1943            $self->{read_until}->($self->{ca}->{value},
1944                                  q["'=& >],
1945                                  length $self->{ca}->{value});
1946    
1947          ## Stay in the state          ## Stay in the state
1948          !!!next-input-character;          !!!next-input-character;
1949          redo A;          redo A;
1950        }        }
     } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {  
       my $token = $self->_tokenize_attempt_to_consume_an_entity  
           (1,  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '  
            -1);  
   
       unless (defined $token) {  
         !!!cp (117);  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         !!!cp (118);  
         $self->{current_attribute}->{value} .= $token->{data};  
         $self->{current_attribute}->{has_reference} = $token->{has_reference};  
         ## ISSUE: spec says "append the returned character token to the current attribute's value"  
       }  
   
       $self->{state} = $self->{last_attribute_value_state};  
       # next-input-character is already done  
       redo A;  
1951      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
1952        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
1953            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
1954            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
1955            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
1956            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
1957          !!!cp (118);          !!!cp (118);
1958          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1959          !!!next-input-character;          !!!next-input-character;
1960          redo A;          redo A;
1961        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1962          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1963            !!!cp (119);            !!!cp (119);
1964            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1965          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1966            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1967            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1968              !!!cp (120);              !!!cp (120);
1969              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1970            } else {            } else {
# Line 1858  sub _get_next_token ($) { Line 1972  sub _get_next_token ($) {
1972              !!!cp (121);              !!!cp (121);
1973            }            }
1974          } else {          } else {
1975            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1976          }          }
1977          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1978          !!!next-input-character;          !!!next-input-character;
1979    
1980          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1981    
1982          redo A;          redo A;
1983        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1984          !!!cp (122);          !!!cp (122);
1985          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1986          !!!next-input-character;          !!!next-input-character;
1987          redo A;          redo A;
1988        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1989          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1990          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1991            !!!cp (122.3);            !!!cp (122.3);
1992            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1993          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1994            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1995              !!!cp (122.1);              !!!cp (122.1);
1996              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1997            } else {            } else {
# Line 1885  sub _get_next_token ($) { Line 1999  sub _get_next_token ($) {
1999              !!!cp (122.2);              !!!cp (122.2);
2000            }            }
2001          } else {          } else {
2002            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2003          }          }
2004          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2005          ## Reconsume.          ## Reconsume.
2006          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2007          redo A;          redo A;
2008        } else {        } else {
2009          !!!cp ('124.1');          !!!cp ('124.1');
# Line 1899  sub _get_next_token ($) { Line 2013  sub _get_next_token ($) {
2013          redo A;          redo A;
2014        }        }
2015      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2016        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2017          if ($self->{current_token}->{type} == END_TAG_TOKEN) {          if ($self->{ct}->{type} == END_TAG_TOKEN) {
2018            !!!cp ('124.2');            !!!cp ('124.2');
2019            !!!parse-error (type => 'nestc', token => $self->{current_token});            !!!parse-error (type => 'nestc', token => $self->{ct});
2020            ## TODO: Different type than slash in start tag            ## TODO: Different type than slash in start tag
2021            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2022            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2023              !!!cp ('124.4');              !!!cp ('124.4');
2024              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2025            } else {            } else {
# Line 1920  sub _get_next_token ($) { Line 2034  sub _get_next_token ($) {
2034          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2035          !!!next-input-character;          !!!next-input-character;
2036    
2037          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2038    
2039          redo A;          redo A;
2040        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2041          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
2042          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2043            !!!cp (124.7);            !!!cp (124.7);
2044            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2045          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2046            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2047              !!!cp (124.5);              !!!cp (124.5);
2048              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2049            } else {            } else {
# Line 1937  sub _get_next_token ($) { Line 2051  sub _get_next_token ($) {
2051              !!!cp (124.6);              !!!cp (124.6);
2052            }            }
2053          } else {          } else {
2054            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2055          }          }
2056          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2057          ## Reconsume.          ## Reconsume.
2058          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2059          redo A;          redo A;
2060        } else {        } else {
2061          !!!cp ('124.4');          !!!cp ('124.4');
# Line 1953  sub _get_next_token ($) { Line 2067  sub _get_next_token ($) {
2067        }        }
2068      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2069        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
         
       ## NOTE: Set by the previous state  
       #my $token = {type => COMMENT_TOKEN, data => ''};  
2070    
2071        BC: {        ## NOTE: Unlike spec's "bogus comment state", this implementation
2072          if ($self->{next_char} == 0x003E) { # >        ## consumes characters one-by-one basis.
2073            !!!cp (124);        
2074            $self->{state} = DATA_STATE;        if ($self->{nc} == 0x003E) { # >
2075            !!!next-input-character;          !!!cp (124);
2076            $self->{state} = DATA_STATE;
2077            !!!emit ($self->{current_token}); # comment          !!!next-input-character;
   
           redo A;  
         } elsif ($self->{next_char} == -1) {  
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2078    
2079            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2080            redo A;
2081          } elsif ($self->{nc} == -1) {
2082            !!!cp (125);
2083            $self->{state} = DATA_STATE;
2084            ## reconsume
2085    
2086            redo A;          !!!emit ($self->{ct}); # comment
2087          } else {          redo A;
2088            !!!cp (126);        } else {
2089            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          !!!cp (126);
2090            !!!next-input-character;          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2091            redo BC;          $self->{read_until}->($self->{ct}->{data},
2092          }                                q[>],
2093        } # BC                                length $self->{ct}->{data});
2094    
2095        die "$0: _get_next_token: unexpected case [BC]";          ## Stay in the state.
2096            !!!next-input-character;
2097            redo A;
2098          }
2099      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2100        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);  
   
       my @next_char;  
       push @next_char, $self->{next_char};  
2101                
2102        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2103            !!!cp (133);
2104            $self->{state} = MD_HYPHEN_STATE;
2105          !!!next-input-character;          !!!next-input-character;
2106          push @next_char, $self->{next_char};          redo A;
2107          if ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2108            !!!cp (127);                 $self->{nc} == 0x0064) { # d
2109            $self->{current_token} = {type => COMMENT_TOKEN, data => '',          ## ASCII case-insensitive.
2110                                      line => $l, column => $c,          !!!cp (130);
2111                                     };          $self->{state} = MD_DOCTYPE_STATE;
2112            $self->{state} = COMMENT_START_STATE;          $self->{s_kwd} = chr $self->{nc};
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (128);  
         }  
       } elsif ($self->{next_char} == 0x0044 or # D  
                $self->{next_char} == 0x0064) { # d  
2113          !!!next-input-character;          !!!next-input-character;
2114          push @next_char, $self->{next_char};          redo A;
         if ($self->{next_char} == 0x004F or # O  
             $self->{next_char} == 0x006F) { # o  
           !!!next-input-character;  
           push @next_char, $self->{next_char};  
           if ($self->{next_char} == 0x0043 or # C  
               $self->{next_char} == 0x0063) { # c  
             !!!next-input-character;  
             push @next_char, $self->{next_char};  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               push @next_char, $self->{next_char};  
               if ($self->{next_char} == 0x0059 or # Y  
                   $self->{next_char} == 0x0079) { # y  
                 !!!next-input-character;  
                 push @next_char, $self->{next_char};  
                 if ($self->{next_char} == 0x0050 or # P  
                     $self->{next_char} == 0x0070) { # p  
                   !!!next-input-character;  
                   push @next_char, $self->{next_char};  
                   if ($self->{next_char} == 0x0045 or # E  
                       $self->{next_char} == 0x0065) { # e  
                     !!!cp (129);  
                     ## TODO: What a stupid code this is!  
                     $self->{state} = DOCTYPE_STATE;  
                     $self->{current_token} = {type => DOCTYPE_TOKEN,  
                                               quirks => 1,  
                                               line => $l, column => $c,  
                                              };  
                     !!!next-input-character;  
                     redo A;  
                   } else {  
                     !!!cp (130);  
                   }  
                 } else {  
                   !!!cp (131);  
                 }  
               } else {  
                 !!!cp (132);  
               }  
             } else {  
               !!!cp (133);  
             }  
           } else {  
             !!!cp (134);  
           }  
         } else {  
           !!!cp (135);  
         }  
2115        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2116                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2117                 $self->{next_char} == 0x005B) { # [                 $self->{nc} == 0x005B) { # [
2118            !!!cp (135.4);                
2119            $self->{state} = MD_CDATA_STATE;
2120            $self->{s_kwd} = '[';
2121          !!!next-input-character;          !!!next-input-character;
2122          push @next_char, $self->{next_char};          redo A;
         if ($self->{next_char} == 0x0043) { # C  
           !!!next-input-character;  
           push @next_char, $self->{next_char};  
           if ($self->{next_char} == 0x0044) { # D  
             !!!next-input-character;  
             push @next_char, $self->{next_char};  
             if ($self->{next_char} == 0x0041) { # A  
               !!!next-input-character;  
               push @next_char, $self->{next_char};  
               if ($self->{next_char} == 0x0054) { # T  
                 !!!next-input-character;  
                 push @next_char, $self->{next_char};  
                 if ($self->{next_char} == 0x0041) { # A  
                   !!!next-input-character;  
                   push @next_char, $self->{next_char};  
                   if ($self->{next_char} == 0x005B) { # [  
                     !!!cp (135.1);  
                     $self->{state} = CDATA_BLOCK_STATE;  
                     !!!next-input-character;  
                     redo A;  
                   } else {  
                     !!!cp (135.2);  
                   }  
                 } else {  
                   !!!cp (135.3);  
                 }  
               } else {  
                 !!!cp (135.4);                  
               }  
             } else {  
               !!!cp (135.5);  
             }  
           } else {  
             !!!cp (135.6);  
           }  
         } else {  
           !!!cp (135.7);  
         }  
2123        } else {        } else {
2124          !!!cp (136);          !!!cp (136);
2125        }        }
2126    
2127        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2128        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2129        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2130          ## Reconsume.
2131        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2132        $self->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2133                                  line => $l, column => $c,                                  line => $self->{line_prev},
2134                                    column => $self->{column_prev} - 1,
2135                                 };                                 };
2136        redo A;        redo A;
2137              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2138        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2139        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2140            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2141                                      line => $self->{line_prev},
2142                                      column => $self->{column_prev} - 2,
2143                                     };
2144            $self->{state} = COMMENT_START_STATE;
2145            !!!next-input-character;
2146            redo A;
2147          } else {
2148            !!!cp (128);
2149            !!!parse-error (type => 'bogus comment',
2150                            line => $self->{line_prev},
2151                            column => $self->{column_prev} - 2);
2152            $self->{state} = BOGUS_COMMENT_STATE;
2153            ## Reconsume.
2154            $self->{ct} = {type => COMMENT_TOKEN,
2155                                      data => '-',
2156                                      line => $self->{line_prev},
2157                                      column => $self->{column_prev} - 2,
2158                                     };
2159            redo A;
2160          }
2161        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2162          ## ASCII case-insensitive.
2163          if ($self->{nc} == [
2164                undef,
2165                0x004F, # O
2166                0x0043, # C
2167                0x0054, # T
2168                0x0059, # Y
2169                0x0050, # P
2170              ]->[length $self->{s_kwd}] or
2171              $self->{nc} == [
2172                undef,
2173                0x006F, # o
2174                0x0063, # c
2175                0x0074, # t
2176                0x0079, # y
2177                0x0070, # p
2178              ]->[length $self->{s_kwd}]) {
2179            !!!cp (131);
2180            ## Stay in the state.
2181            $self->{s_kwd} .= chr $self->{nc};
2182            !!!next-input-character;
2183            redo A;
2184          } elsif ((length $self->{s_kwd}) == 6 and
2185                   ($self->{nc} == 0x0045 or # E
2186                    $self->{nc} == 0x0065)) { # e
2187            !!!cp (129);
2188            $self->{state} = DOCTYPE_STATE;
2189            $self->{ct} = {type => DOCTYPE_TOKEN,
2190                                      quirks => 1,
2191                                      line => $self->{line_prev},
2192                                      column => $self->{column_prev} - 7,
2193                                     };
2194            !!!next-input-character;
2195            redo A;
2196          } else {
2197            !!!cp (132);        
2198            !!!parse-error (type => 'bogus comment',
2199                            line => $self->{line_prev},
2200                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2201            $self->{state} = BOGUS_COMMENT_STATE;
2202            ## Reconsume.
2203            $self->{ct} = {type => COMMENT_TOKEN,
2204                                      data => $self->{s_kwd},
2205                                      line => $self->{line_prev},
2206                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2207                                     };
2208            redo A;
2209          }
2210        } elsif ($self->{state} == MD_CDATA_STATE) {
2211          if ($self->{nc} == {
2212                '[' => 0x0043, # C
2213                '[C' => 0x0044, # D
2214                '[CD' => 0x0041, # A
2215                '[CDA' => 0x0054, # T
2216                '[CDAT' => 0x0041, # A
2217              }->{$self->{s_kwd}}) {
2218            !!!cp (135.1);
2219            ## Stay in the state.
2220            $self->{s_kwd} .= chr $self->{nc};
2221            !!!next-input-character;
2222            redo A;
2223          } elsif ($self->{s_kwd} eq '[CDATA' and
2224                   $self->{nc} == 0x005B) { # [
2225            !!!cp (135.2);
2226            $self->{ct} = {type => CHARACTER_TOKEN,
2227                                      data => '',
2228                                      line => $self->{line_prev},
2229                                      column => $self->{column_prev} - 7};
2230            $self->{state} = CDATA_SECTION_STATE;
2231            !!!next-input-character;
2232            redo A;
2233          } else {
2234            !!!cp (135.3);
2235            !!!parse-error (type => 'bogus comment',
2236                            line => $self->{line_prev},
2237                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2238            $self->{state} = BOGUS_COMMENT_STATE;
2239            ## Reconsume.
2240            $self->{ct} = {type => COMMENT_TOKEN,
2241                                      data => $self->{s_kwd},
2242                                      line => $self->{line_prev},
2243                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2244                                     };
2245            redo A;
2246          }
2247      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2248        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2249          !!!cp (137);          !!!cp (137);
2250          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2251          !!!next-input-character;          !!!next-input-character;
2252          redo A;          redo A;
2253        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2254          !!!cp (138);          !!!cp (138);
2255          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2256          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2257          !!!next-input-character;          !!!next-input-character;
2258    
2259          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2260    
2261          redo A;          redo A;
2262        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2263          !!!cp (139);          !!!cp (139);
2264          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2265          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2266          ## reconsume          ## reconsume
2267    
2268          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2269    
2270          redo A;          redo A;
2271        } else {        } else {
2272          !!!cp (140);          !!!cp (140);
2273          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2274              .= chr ($self->{next_char});              .= chr ($self->{nc});
2275          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2276          !!!next-input-character;          !!!next-input-character;
2277          redo A;          redo A;
2278        }        }
2279      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2280        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2281          !!!cp (141);          !!!cp (141);
2282          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2283          !!!next-input-character;          !!!next-input-character;
2284          redo A;          redo A;
2285        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2286          !!!cp (142);          !!!cp (142);
2287          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2288          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2289          !!!next-input-character;          !!!next-input-character;
2290    
2291          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2292    
2293          redo A;          redo A;
2294        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2295          !!!cp (143);          !!!cp (143);
2296          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2297          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2298          ## reconsume          ## reconsume
2299    
2300          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2301    
2302          redo A;          redo A;
2303        } else {        } else {
2304          !!!cp (144);          !!!cp (144);
2305          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2306              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2307          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2308          !!!next-input-character;          !!!next-input-character;
2309          redo A;          redo A;
2310        }        }
2311      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2312        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2313          !!!cp (145);          !!!cp (145);
2314          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2315          !!!next-input-character;          !!!next-input-character;
2316          redo A;          redo A;
2317        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2318          !!!cp (146);          !!!cp (146);
2319          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2320          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2321          ## reconsume          ## reconsume
2322    
2323          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2324    
2325          redo A;          redo A;
2326        } else {        } else {
2327          !!!cp (147);          !!!cp (147);
2328          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2329            $self->{read_until}->($self->{ct}->{data},
2330                                  q[-],
2331                                  length $self->{ct}->{data});
2332    
2333          ## Stay in the state          ## Stay in the state
2334          !!!next-input-character;          !!!next-input-character;
2335          redo A;          redo A;
2336        }        }
2337      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2338        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2339          !!!cp (148);          !!!cp (148);
2340          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2344          !!!cp (149);          !!!cp (149);
2345          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2346          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2347          ## reconsume          ## reconsume
2348    
2349          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2350    
2351          redo A;          redo A;
2352        } else {        } else {
2353          !!!cp (150);          !!!cp (150);
2354          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2355          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2356          !!!next-input-character;          !!!next-input-character;
2357          redo A;          redo A;
2358        }        }
2359      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2360        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2361          !!!cp (151);          !!!cp (151);
2362          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2363          !!!next-input-character;          !!!next-input-character;
2364    
2365          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2366    
2367          redo A;          redo A;
2368        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2369          !!!cp (152);          !!!cp (152);
2370          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2371                          line => $self->{line_prev},                          line => $self->{line_prev},
2372                          column => $self->{column_prev});                          column => $self->{column_prev});
2373          $self->{current_token}->{data} .= '-'; # comment          $self->{ct}->{data} .= '-'; # comment
2374          ## Stay in the state          ## Stay in the state
2375          !!!next-input-character;          !!!next-input-character;
2376          redo A;          redo A;
2377        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2378          !!!cp (153);          !!!cp (153);
2379          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2380          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2381          ## reconsume          ## reconsume
2382    
2383          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2384    
2385          redo A;          redo A;
2386        } else {        } else {
# Line 2256  sub _get_next_token ($) { Line 2388  sub _get_next_token ($) {
2388          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2389                          line => $self->{line_prev},                          line => $self->{line_prev},
2390                          column => $self->{column_prev});                          column => $self->{column_prev});
2391          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2392          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2393          !!!next-input-character;          !!!next-input-character;
2394          redo A;          redo A;
2395        }        }
2396      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2397        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
2398            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
2399            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
2400            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
2401            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
2402          !!!cp (155);          !!!cp (155);
2403          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2404          !!!next-input-character;          !!!next-input-character;
# Line 2279  sub _get_next_token ($) { Line 2411  sub _get_next_token ($) {
2411          redo A;          redo A;
2412        }        }
2413      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2414        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
2415            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
2416            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
2417            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
2418            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
2419          !!!cp (157);          !!!cp (157);
2420          ## Stay in the state          ## Stay in the state
2421          !!!next-input-character;          !!!next-input-character;
2422          redo A;          redo A;
2423        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2424          !!!cp (158);          !!!cp (158);
2425          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2426          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2427          !!!next-input-character;          !!!next-input-character;
2428    
2429          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2430    
2431          redo A;          redo A;
2432        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2433          !!!cp (159);          !!!cp (159);
2434          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2435          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2436          ## reconsume          ## reconsume
2437    
2438          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2439    
2440          redo A;          redo A;
2441        } else {        } else {
2442          !!!cp (160);          !!!cp (160);
2443          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2444          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
2445  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2446          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2447          !!!next-input-character;          !!!next-input-character;
# Line 2317  sub _get_next_token ($) { Line 2449  sub _get_next_token ($) {
2449        }        }
2450      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2451  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2452        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
2453            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
2454            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
2455            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
2456            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
2457          !!!cp (161);          !!!cp (161);
2458          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2459          !!!next-input-character;          !!!next-input-character;
2460          redo A;          redo A;
2461        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2462          !!!cp (162);          !!!cp (162);
2463          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2464          !!!next-input-character;          !!!next-input-character;
2465    
2466          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2467    
2468          redo A;          redo A;
2469        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2470          !!!cp (163);          !!!cp (163);
2471          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2472          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2473          ## reconsume          ## reconsume
2474    
2475          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2476          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2477    
2478          redo A;          redo A;
2479        } else {        } else {
2480          !!!cp (164);          !!!cp (164);
2481          $self->{current_token}->{name}          $self->{ct}->{name}
2482            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2483          ## Stay in the state          ## Stay in the state
2484          !!!next-input-character;          !!!next-input-character;
2485          redo A;          redo A;
2486        }        }
2487      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2488        if ($self->{next_char} == 0x0009 or # HT        if ($self->{nc} == 0x0009 or # HT
2489            $self->{next_char} == 0x000A or # LF            $self->{nc} == 0x000A or # LF
2490            $self->{next_char} == 0x000B or # VT            $self->{nc} == 0x000B or # VT
2491            $self->{next_char} == 0x000C or # FF            $self->{nc} == 0x000C or # FF
2492            $self->{next_char} == 0x0020) { # SP            $self->{nc} == 0x0020) { # SP
2493          !!!cp (165);          !!!cp (165);
2494          ## Stay in the state          ## Stay in the state
2495          !!!next-input-character;          !!!next-input-character;
2496          redo A;          redo A;
2497        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2498          !!!cp (166);          !!!cp (166);
2499          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2500          !!!next-input-character;          !!!next-input-character;
2501    
2502          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2503    
2504          redo A;          redo A;
2505        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2506          !!!cp (167);          !!!cp (167);
2507          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2508          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2509          ## reconsume          ## reconsume
2510    
2511          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2512          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2513    
2514          redo A;          redo A;
2515        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2516                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2517            $self->{state} = PUBLIC_STATE;
2518            $self->{s_kwd} = chr $self->{nc};
2519          !!!next-input-character;          !!!next-input-character;
2520          if ($self->{next_char} == 0x0055 or # U          redo A;
2521              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2522            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2523            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2524                $self->{next_char} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_char} == 0x004C or # L  
                 $self->{next_char} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0049 or # I  
                   $self->{next_char} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x0043 or # C  
                     $self->{next_char} == 0x0063) { # c  
                   !!!cp (168);  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (169);  
                 }  
               } else {  
                 !!!cp (170);  
               }  
             } else {  
               !!!cp (171);  
             }  
           } else {  
             !!!cp (172);  
           }  
         } else {  
           !!!cp (173);  
         }  
   
         #  
       } elsif ($self->{next_char} == 0x0053 or # S  
                $self->{next_char} == 0x0073) { # s  
2525          !!!next-input-character;          !!!next-input-character;
2526          if ($self->{next_char} == 0x0059 or # Y          redo A;
             $self->{next_char} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_char} == 0x0053 or # S  
               $self->{next_char} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0045 or # E  
                   $self->{next_char} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x004D or # M  
                     $self->{next_char} == 0x006D) { # m  
                   !!!cp (174);  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (175);  
                 }  
               } else {  
                 !!!cp (176);  
               }  
             } else {  
               !!!cp (177);  
             }  
           } else {  
             !!!cp (178);  
           }  
         } else {  
           !!!cp (179);  
         }  
   
         #  
2527        } else {        } else {
2528          !!!cp (180);          !!!cp (180);
2529            !!!parse-error (type => 'string after DOCTYPE name');
2530            $self->{ct}->{quirks} = 1;
2531    
2532            $self->{state} = BOGUS_DOCTYPE_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534          #          redo A;
2535        }        }
2536        } elsif ($self->{state} == PUBLIC_STATE) {
2537          ## ASCII case-insensitive
2538          if ($self->{nc} == [
2539                undef,
2540                0x0055, # U
2541                0x0042, # B
2542                0x004C, # L
2543                0x0049, # I
2544              ]->[length $self->{s_kwd}] or
2545              $self->{nc} == [
2546                undef,
2547                0x0075, # u
2548                0x0062, # b
2549                0x006C, # l
2550                0x0069, # i
2551              ]->[length $self->{s_kwd}]) {
2552            !!!cp (175);
2553            ## Stay in the state.
2554            $self->{s_kwd} .= chr $self->{nc};
2555            !!!next-input-character;
2556            redo A;
2557          } elsif ((length $self->{s_kwd}) == 5 and
2558                   ($self->{nc} == 0x0043 or # C
2559                    $self->{nc} == 0x0063)) { # c
2560            !!!cp (168);
2561            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2562            !!!next-input-character;
2563            redo A;
2564          } else {
2565            !!!cp (169);
2566            !!!parse-error (type => 'string after DOCTYPE name',
2567                            line => $self->{line_prev},
2568                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2569            $self->{ct}->{quirks} = 1;
2570    
2571        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2572        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2573            redo A;
2574          }
2575        } elsif ($self->{state} == SYSTEM_STATE) {
2576          ## ASCII case-insensitive
2577          if ($self->{nc} == [
2578                undef,
2579                0x0059, # Y
2580                0x0053, # S
2581                0x0054, # T
2582                0x0045, # E
2583              ]->[length $self->{s_kwd}] or
2584              $self->{nc} == [
2585                undef,
2586                0x0079, # y
2587                0x0073, # s
2588                0x0074, # t
2589                0x0065, # e
2590              ]->[length $self->{s_kwd}]) {
2591            !!!cp (170);
2592            ## Stay in the state.
2593            $self->{s_kwd} .= chr $self->{nc};
2594            !!!next-input-character;
2595            redo A;
2596          } elsif ((length $self->{s_kwd}) == 5 and
2597                   ($self->{nc} == 0x004D or # M
2598                    $self->{nc} == 0x006D)) { # m
2599            !!!cp (171);
2600            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2601            !!!next-input-character;
2602            redo A;
2603          } else {
2604            !!!cp (172);
2605            !!!parse-error (type => 'string after DOCTYPE name',
2606                            line => $self->{line_prev},
2607                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2608            $self->{ct}->{quirks} = 1;
2609    
2610        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2611        # next-input-character is already done          ## Reconsume.
2612        redo A;          redo A;
2613          }
2614      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2615        if ({        if ({
2616              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2617              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2618            }->{$self->{next_char}}) {            }->{$self->{nc}}) {
2619          !!!cp (181);          !!!cp (181);
2620          ## Stay in the state          ## Stay in the state
2621          !!!next-input-character;          !!!next-input-character;
2622          redo A;          redo A;
2623        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2624          !!!cp (182);          !!!cp (182);
2625          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2626          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2627          !!!next-input-character;          !!!next-input-character;
2628          redo A;          redo A;
2629        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2630          !!!cp (183);          !!!cp (183);
2631          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2632          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2633          !!!next-input-character;          !!!next-input-character;
2634          redo A;          redo A;
2635        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2636          !!!cp (184);          !!!cp (184);
2637          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2638    
2639          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2640          !!!next-input-character;          !!!next-input-character;
2641    
2642          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2643          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2644    
2645          redo A;          redo A;
2646        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2647          !!!cp (185);          !!!cp (185);
2648          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2649    
2650          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2651          ## reconsume          ## reconsume
2652    
2653          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2654          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2655    
2656          redo A;          redo A;
2657        } else {        } else {
2658          !!!cp (186);          !!!cp (186);
2659          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2660          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2661    
2662          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        }        }
2666      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2667        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2668          !!!cp (187);          !!!cp (187);
2669          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671          redo A;          redo A;
2672        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2673          !!!cp (188);          !!!cp (188);
2674          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2675    
2676          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678    
2679          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2680          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2681    
2682          redo A;          redo A;
2683        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2684          !!!cp (189);          !!!cp (189);
2685          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2686    
2687          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2688          ## reconsume          ## reconsume
2689    
2690          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2691          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2692    
2693          redo A;          redo A;
2694        } else {        } else {
2695          !!!cp (190);          !!!cp (190);
2696          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2697              .= chr $self->{next_char};              .= chr $self->{nc};
2698            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2699                                  length $self->{ct}->{pubid});
2700    
2701          ## Stay in the state          ## Stay in the state
2702          !!!next-input-character;          !!!next-input-character;
2703          redo A;          redo A;
2704        }        }
2705      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2706        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2707          !!!cp (191);          !!!cp (191);
2708          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2709          !!!next-input-character;          !!!next-input-character;
2710          redo A;          redo A;
2711        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2712          !!!cp (192);          !!!cp (192);
2713          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2714    
2715          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2716          !!!next-input-character;          !!!next-input-character;
2717    
2718          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2719          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2720    
2721          redo A;          redo A;
2722        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2723          !!!cp (193);          !!!cp (193);
2724          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2725    
2726          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2727          ## reconsume          ## reconsume
2728    
2729          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2730          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2731    
2732          redo A;          redo A;
2733        } else {        } else {
2734          !!!cp (194);          !!!cp (194);
2735          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2736              .= chr $self->{next_char};              .= chr $self->{nc};
2737            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2738                                  length $self->{ct}->{pubid});
2739    
2740          ## Stay in the state          ## Stay in the state
2741          !!!next-input-character;          !!!next-input-character;
2742          redo A;          redo A;
# Line 2596  sub _get_next_token ($) { Line 2745  sub _get_next_token ($) {
2745        if ({        if ({
2746              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2747              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2748            }->{$self->{next_char}}) {            }->{$self->{nc}}) {
2749          !!!cp (195);          !!!cp (195);
2750          ## Stay in the state          ## Stay in the state
2751          !!!next-input-character;          !!!next-input-character;
2752          redo A;          redo A;
2753        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2754          !!!cp (196);          !!!cp (196);
2755          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2756          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2757          !!!next-input-character;          !!!next-input-character;
2758          redo A;          redo A;
2759        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2760          !!!cp (197);          !!!cp (197);
2761          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2762          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2763          !!!next-input-character;          !!!next-input-character;
2764          redo A;          redo A;
2765        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2766          !!!cp (198);          !!!cp (198);
2767          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2768          !!!next-input-character;          !!!next-input-character;
2769    
2770          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2771    
2772          redo A;          redo A;
2773        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2774          !!!cp (199);          !!!cp (199);
2775          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2776    
2777          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2778          ## reconsume          ## reconsume
2779    
2780          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2781          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2782    
2783          redo A;          redo A;
2784        } else {        } else {
2785          !!!cp (200);          !!!cp (200);
2786          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2787          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2788    
2789          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2790          !!!next-input-character;          !!!next-input-character;
# Line 2645  sub _get_next_token ($) { Line 2794  sub _get_next_token ($) {
2794        if ({        if ({
2795              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2796              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2797            }->{$self->{next_char}}) {            }->{$self->{nc}}) {
2798          !!!cp (201);          !!!cp (201);
2799          ## Stay in the state          ## Stay in the state
2800          !!!next-input-character;          !!!next-input-character;
2801          redo A;          redo A;
2802        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2803          !!!cp (202);          !!!cp (202);
2804          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2805          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2806          !!!next-input-character;          !!!next-input-character;
2807          redo A;          redo A;
2808        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2809          !!!cp (203);          !!!cp (203);
2810          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2811          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2812          !!!next-input-character;          !!!next-input-character;
2813          redo A;          redo A;
2814        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2815          !!!cp (204);          !!!cp (204);
2816          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2817          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2818          !!!next-input-character;          !!!next-input-character;
2819    
2820          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2821          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2822    
2823          redo A;          redo A;
2824        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2825          !!!cp (205);          !!!cp (205);
2826          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2827    
2828          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2829          ## reconsume          ## reconsume
2830    
2831          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2832          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2833    
2834          redo A;          redo A;
2835        } else {        } else {
2836          !!!cp (206);          !!!cp (206);
2837          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2838          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2839    
2840          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2841          !!!next-input-character;          !!!next-input-character;
2842          redo A;          redo A;
2843        }        }
2844      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2845        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2846          !!!cp (207);          !!!cp (207);
2847          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2848          !!!next-input-character;          !!!next-input-character;
2849          redo A;          redo A;
2850        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2851          !!!cp (208);          !!!cp (208);
2852          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2853    
2854          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2855          !!!next-input-character;          !!!next-input-character;
2856    
2857          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2858          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2859    
2860          redo A;          redo A;
2861        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2862          !!!cp (209);          !!!cp (209);
2863          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2864    
2865          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2866          ## reconsume          ## reconsume
2867    
2868          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2869          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2870    
2871          redo A;          redo A;
2872        } else {        } else {
2873          !!!cp (210);          !!!cp (210);
2874          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2875              .= chr $self->{next_char};              .= chr $self->{nc};
2876            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2877                                  length $self->{ct}->{sysid});
2878    
2879          ## Stay in the state          ## Stay in the state
2880          !!!next-input-character;          !!!next-input-character;
2881          redo A;          redo A;
2882        }        }
2883      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2884        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2885          !!!cp (211);          !!!cp (211);
2886          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2887          !!!next-input-character;          !!!next-input-character;
2888          redo A;          redo A;
2889        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2890          !!!cp (212);          !!!cp (212);
2891          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2892    
2893          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2894          !!!next-input-character;          !!!next-input-character;
2895    
2896          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2897          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2898    
2899          redo A;          redo A;
2900        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2901          !!!cp (213);          !!!cp (213);
2902          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2903    
2904          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2905          ## reconsume          ## reconsume
2906    
2907          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2908          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2909    
2910          redo A;          redo A;
2911        } else {        } else {
2912          !!!cp (214);          !!!cp (214);
2913          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2914              .= chr $self->{next_char};              .= chr $self->{nc};
2915            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2916                                  length $self->{ct}->{sysid});
2917    
2918          ## Stay in the state          ## Stay in the state
2919          !!!next-input-character;          !!!next-input-character;
2920          redo A;          redo A;
# Line 2768  sub _get_next_token ($) { Line 2923  sub _get_next_token ($) {
2923        if ({        if ({
2924              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2925              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2926            }->{$self->{next_char}}) {            }->{$self->{nc}}) {
2927          !!!cp (215);          !!!cp (215);
2928          ## Stay in the state          ## Stay in the state
2929          !!!next-input-character;          !!!next-input-character;
2930          redo A;          redo A;
2931        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2932          !!!cp (216);          !!!cp (216);
2933          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2934          !!!next-input-character;          !!!next-input-character;
2935    
2936          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2937    
2938          redo A;          redo A;
2939        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2940          !!!cp (217);          !!!cp (217);
2941          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2942          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2943          ## reconsume          ## reconsume
2944    
2945          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2946          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2947    
2948          redo A;          redo A;
2949        } else {        } else {
2950          !!!cp (218);          !!!cp (218);
2951          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2952          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2953    
2954          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956          redo A;          redo A;
2957        }        }
2958      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2959        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2960          !!!cp (219);          !!!cp (219);
2961          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2962          !!!next-input-character;          !!!next-input-character;
2963    
2964          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2965    
2966          redo A;          redo A;
2967        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2968          !!!cp (220);          !!!cp (220);
2969          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2970          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2971          ## reconsume          ## reconsume
2972    
2973          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2974    
2975          redo A;          redo A;
2976        } else {        } else {
2977          !!!cp (221);          !!!cp (221);
2978            my $s = '';
2979            $self->{read_until}->($s, q[>], 0);
2980    
2981          ## Stay in the state          ## Stay in the state
2982          !!!next-input-character;          !!!next-input-character;
2983          redo A;          redo A;
2984        }        }
2985      } elsif ($self->{state} == CDATA_BLOCK_STATE) {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
2986        my $s = '';        ## NOTE: "CDATA section state" in the state is jointly implemented
2987          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
2988          ## and |CDATA_SECTION_MSE2_STATE|.
2989                
2990        my ($l, $c) = ($self->{line}, $self->{column});        if ($self->{nc} == 0x005D) { # ]
2991            !!!cp (221.1);
2992        CS: while ($self->{next_char} != -1) {          $self->{state} = CDATA_SECTION_MSE1_STATE;
2993          if ($self->{next_char} == 0x005D) { # ]          !!!next-input-character;
2994            !!!next-input-character;          redo A;
2995            if ($self->{next_char} == 0x005D) { # ]        } elsif ($self->{nc} == -1) {
2996              !!!next-input-character;          $self->{state} = DATA_STATE;
             MDC: {  
               if ($self->{next_char} == 0x003E) { # >  
                 !!!cp (221.1);  
                 !!!next-input-character;  
                 last CS;  
               } elsif ($self->{next_char} == 0x005D) { # ]  
                 !!!cp (221.2);  
                 $s .= ']';  
                 !!!next-input-character;  
                 redo MDC;  
               } else {  
                 !!!cp (221.3);  
                 $s .= ']]';  
                 #  
               }  
             } # MDC  
           } else {  
             !!!cp (221.4);  
             $s .= ']';  
             #  
           }  
         } else {  
           !!!cp (221.5);  
           #  
         }  
         $s .= chr $self->{next_char};  
2997          !!!next-input-character;          !!!next-input-character;
2998        } # CS          if (length $self->{ct}->{data}) { # character
2999              !!!cp (221.2);
3000              !!!emit ($self->{ct}); # character
3001            } else {
3002              !!!cp (221.3);
3003              ## No token to emit. $self->{ct} is discarded.
3004            }        
3005            redo A;
3006          } else {
3007            !!!cp (221.4);
3008            $self->{ct}->{data} .= chr $self->{nc};
3009            $self->{read_until}->($self->{ct}->{data},
3010                                  q<]>,
3011                                  length $self->{ct}->{data});
3012    
3013        $self->{state} = DATA_STATE;          ## Stay in the state.
3014        ## next-input-character done or EOF, which is reconsumed.          !!!next-input-character;
3015            redo A;
3016          }
3017    
3018        if (length $s) {        ## ISSUE: "text tokens" in spec.
3019        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3020          if ($self->{nc} == 0x005D) { # ]
3021            !!!cp (221.5);
3022            $self->{state} = CDATA_SECTION_MSE2_STATE;
3023            !!!next-input-character;
3024            redo A;
3025          } else {
3026          !!!cp (221.6);          !!!cp (221.6);
3027          !!!emit ({type => CHARACTER_TOKEN, data => $s,          $self->{ct}->{data} .= ']';
3028                    line => $l, column => $c});          $self->{state} = CDATA_SECTION_STATE;
3029            ## Reconsume.
3030            redo A;
3031          }
3032        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3033          if ($self->{nc} == 0x003E) { # >
3034            $self->{state} = DATA_STATE;
3035            !!!next-input-character;
3036            if (length $self->{ct}->{data}) { # character
3037              !!!cp (221.7);
3038              !!!emit ($self->{ct}); # character
3039            } else {
3040              !!!cp (221.8);
3041              ## No token to emit. $self->{ct} is discarded.
3042            }
3043            redo A;
3044          } elsif ($self->{nc} == 0x005D) { # ]
3045            !!!cp (221.9); # character
3046            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3047            ## Stay in the state.
3048            !!!next-input-character;
3049            redo A;
3050        } else {        } else {
3051          !!!cp (221.7);          !!!cp (221.11);
3052            $self->{ct}->{data} .= ']]'; # character
3053            $self->{state} = CDATA_SECTION_STATE;
3054            ## Reconsume.
3055            redo A;
3056          }
3057        } elsif ($self->{state} == ENTITY_STATE) {
3058          if ({
3059            0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
3060            0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, &
3061            $self->{entity_add} => 1,
3062          }->{$self->{nc}}) {
3063            !!!cp (1001);
3064            ## Don't consume
3065            ## No error
3066            ## Return nothing.
3067            #
3068          } elsif ($self->{nc} == 0x0023) { # #
3069            !!!cp (999);
3070            $self->{state} = ENTITY_HASH_STATE;
3071            $self->{s_kwd} = '#';
3072            !!!next-input-character;
3073            redo A;
3074          } elsif ((0x0041 <= $self->{nc} and
3075                    $self->{nc} <= 0x005A) or # A..Z
3076                   (0x0061 <= $self->{nc} and
3077                    $self->{nc} <= 0x007A)) { # a..z
3078            !!!cp (998);
3079            require Whatpm::_NamedEntityList;
3080            $self->{state} = ENTITY_NAME_STATE;
3081            $self->{s_kwd} = chr $self->{nc};
3082            $self->{entity__value} = $self->{s_kwd};
3083            $self->{entity__match} = 0;
3084            !!!next-input-character;
3085            redo A;
3086          } else {
3087            !!!cp (1027);
3088            !!!parse-error (type => 'bare ero');
3089            ## Return nothing.
3090            #
3091        }        }
3092    
3093        redo A;        ## NOTE: No character is consumed by the "consume a character
3094          ## reference" algorithm.  In other word, there is an "&" character
3095        ## ISSUE: "text tokens" in spec.        ## that does not introduce a character reference, which would be
3096        ## TODO: Streaming support        ## appended to the parent element or the attribute value in later
3097      } else {        ## process of the tokenizer.
3098        die "$0: $self->{state}: Unknown state";  
3099      }        if ($self->{prev_state} == DATA_STATE) {
3100    } # A            !!!cp (997);
3101            $self->{state} = $self->{prev_state};
3102    die "$0: _get_next_token: unexpected case";          ## Reconsume.
3103  } # _get_next_token          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3104                      line => $self->{line_prev},
3105  sub _tokenize_attempt_to_consume_an_entity ($$$) {                    column => $self->{column_prev},
3106    my ($self, $in_attr, $additional) = @_;                   });
3107            redo A;
3108    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        } else {
3109            !!!cp (996);
3110            $self->{ca}->{value} .= '&';
3111            $self->{state} = $self->{prev_state};
3112            ## Reconsume.
3113            redo A;
3114          }
3115        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3116          if ($self->{nc} == 0x0078 or # x
3117              $self->{nc} == 0x0058) { # X
3118            !!!cp (995);
3119            $self->{state} = HEXREF_X_STATE;
3120            $self->{s_kwd} .= chr $self->{nc};
3121            !!!next-input-character;
3122            redo A;
3123          } elsif (0x0030 <= $self->{nc} and
3124                   $self->{nc} <= 0x0039) { # 0..9
3125            !!!cp (994);
3126            $self->{state} = NCR_NUM_STATE;
3127            $self->{s_kwd} = $self->{nc} - 0x0030;
3128            !!!next-input-character;
3129            redo A;
3130          } else {
3131            !!!parse-error (type => 'bare nero',
3132                            line => $self->{line_prev},
3133                            column => $self->{column_prev} - 1);
3134    
3135    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3136         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3137         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3138         $additional => 1,  
3139        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3140      !!!cp (1001);            !!!cp (1019);
3141      ## Don't consume            $self->{state} = $self->{prev_state};
3142      ## No error            ## Reconsume.
3143      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3144    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3145      !!!next-input-character;                      line => $self->{line_prev},
3146      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3147          $self->{next_char} == 0x0058) { # X                     });
3148        my $code;            redo A;
       X: {  
         my $x_char = $self->{next_char};  
         !!!next-input-character;  
         if (0x0030 <= $self->{next_char} and  
             $self->{next_char} <= 0x0039) { # 0..9  
           !!!cp (1002);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0066) { # a..f  
           !!!cp (1003);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0046) { # A..F  
           !!!cp (1004);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!cp (1005);  
           !!!parse-error (type => 'bare hcro', line => $l, column => $c);  
           !!!back-next-input-character ($x_char, $self->{next_char});  
           $self->{next_char} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_char} == 0x003B) { # ;  
           !!!cp (1006);  
           !!!next-input-character;  
3149          } else {          } else {
3150            !!!cp (1007);            !!!cp (993);
3151            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3152              $self->{state} = $self->{prev_state};
3153              ## Reconsume.
3154              redo A;
3155          }          }
3156          }
3157          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3158            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3159            !!!parse-error (type => 'invalid character reference',            $self->{nc} <= 0x0039) { # 0..9
                           text => (sprintf 'U+%04X', $code),  
                           line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code > 0x10FFFF) {  
           !!!cp (1009);  
           !!!parse-error (type => 'invalid character reference',  
                           text => (sprintf 'U-%08X', $code),  
                           line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!cp (1010);  
           !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!cp (1011);  
           !!!parse-error (type => 'C1 character reference', text => (sprintf 'U+%04X', $code), line => $l, column => $c);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1,  
                 line => $l, column => $c,  
                };  
       } # X  
     } elsif (0x0030 <= $self->{next_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
3160          !!!cp (1012);          !!!cp (1012);
3161          $code *= 10;          $self->{s_kwd} *= 10;
3162          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3163                    
3164            ## Stay in the state.
3165          !!!next-input-character;          !!!next-input-character;
3166        }          redo A;
3167          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3168          !!!cp (1013);          !!!cp (1013);
3169          !!!next-input-character;          !!!next-input-character;
3170            #
3171        } else {        } else {
3172          !!!cp (1014);          !!!cp (1014);
3173          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3174            ## Reconsume.
3175            #
3176        }        }
3177    
3178          my $code = $self->{s_kwd};
3179          my $l = $self->{line_prev};
3180          my $c = $self->{column_prev};
3181        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
3182          !!!cp (1015);          !!!cp (1015);
3183          !!!parse-error (type => 'invalid character reference',          !!!parse-error (type => 'invalid character reference',
# Line 3016  sub _tokenize_attempt_to_consume_an_enti Line 3202  sub _tokenize_attempt_to_consume_an_enti
3202                          line => $l, column => $c);                          line => $l, column => $c);
3203          $code = $c1_entity_char->{$code};          $code = $c1_entity_char->{$code};
3204        }        }
3205          
3206        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3207                line => $l, column => $c,          !!!cp (992);
3208               };          $self->{state} = $self->{prev_state};
3209      } else {          ## Reconsume.
3210        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3211        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3212        !!!back-next-input-character ($self->{next_char});                   });
3213        $self->{next_char} = 0x0023; # #          redo A;
3214        return undef;        } else {
3215      }          !!!cp (991);
3216    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3217              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3218             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3219              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3220      my $entity_name = chr $self->{next_char};          redo A;
3221      !!!next-input-character;        }
3222        } elsif ($self->{state} == HEXREF_X_STATE) {
3223      my $value = $entity_name;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3224      my $match = 0;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3225      require Whatpm::_NamedEntityList;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3226      our $EntityChar;          # 0..9, A..F, a..f
3227            !!!cp (990);
3228      while (length $entity_name < 30 and          $self->{state} = HEXREF_HEX_STATE;
3229             ## NOTE: Some number greater than the maximum length of entity name          $self->{s_kwd} = 0;
3230             ((0x0041 <= $self->{next_char} and # a          ## Reconsume.
3231               $self->{next_char} <= 0x005A) or # x          redo A;
3232              (0x0061 <= $self->{next_char} and # a        } else {
3233               $self->{next_char} <= 0x007A) or # z          !!!parse-error (type => 'bare hcro',
3234              (0x0030 <= $self->{next_char} and # 0                          line => $self->{line_prev},
3235               $self->{next_char} <= 0x0039) or # 9                          column => $self->{column_prev} - 2);
3236              $self->{next_char} == 0x003B)) { # ;  
3237        $entity_name .= chr $self->{next_char};          ## NOTE: According to the spec algorithm, nothing is returned,
3238        if (defined $EntityChar->{$entity_name}) {          ## and then "&#" followed by "X" or "x" is appended to the parent
3239          if ($self->{next_char} == 0x003B) { # ;          ## element or the attribute value in the later processing.
3240            !!!cp (1020);  
3241            $value = $EntityChar->{$entity_name};          if ($self->{prev_state} == DATA_STATE) {
3242            $match = 1;            !!!cp (1005);
3243            !!!next-input-character;            $self->{state} = $self->{prev_state};
3244            last;            ## Reconsume.
3245              !!!emit ({type => CHARACTER_TOKEN,
3246                        data => '&' . $self->{s_kwd},
3247                        line => $self->{line_prev},
3248                        column => $self->{column_prev} - length $self->{s_kwd},
3249                       });
3250              redo A;
3251          } else {          } else {
3252            !!!cp (1021);            !!!cp (989);
3253            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3254            $match = -1;            $self->{state} = $self->{prev_state};
3255              ## Reconsume.
3256              redo A;
3257            }
3258          }
3259        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3260          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3261            # 0..9
3262            !!!cp (1002);
3263            $self->{s_kwd} *= 0x10;
3264            $self->{s_kwd} += $self->{nc} - 0x0030;
3265            ## Stay in the state.
3266            !!!next-input-character;
3267            redo A;
3268          } elsif (0x0061 <= $self->{nc} and
3269                   $self->{nc} <= 0x0066) { # a..f
3270            !!!cp (1003);
3271            $self->{s_kwd} *= 0x10;
3272            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3273            ## Stay in the state.
3274            !!!next-input-character;
3275            redo A;
3276          } elsif (0x0041 <= $self->{nc} and
3277                   $self->{nc} <= 0x0046) { # A..F
3278            !!!cp (1004);
3279            $self->{s_kwd} *= 0x10;
3280            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3281            ## Stay in the state.
3282            !!!next-input-character;
3283            redo A;
3284          } elsif ($self->{nc} == 0x003B) { # ;
3285            !!!cp (1006);
3286            !!!next-input-character;
3287            #
3288          } else {
3289            !!!cp (1007);
3290            !!!parse-error (type => 'no refc',
3291                            line => $self->{line},
3292                            column => $self->{column});
3293            ## Reconsume.
3294            #
3295          }
3296    
3297          my $code = $self->{s_kwd};
3298          my $l = $self->{line_prev};
3299          my $c = $self->{column_prev};
3300          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
3301            !!!cp (1008);
3302            !!!parse-error (type => 'invalid character reference',
3303                            text => (sprintf 'U+%04X', $code),
3304                            line => $l, column => $c);
3305            $code = 0xFFFD;
3306          } elsif ($code > 0x10FFFF) {
3307            !!!cp (1009);
3308            !!!parse-error (type => 'invalid character reference',
3309                            text => (sprintf 'U-%08X', $code),
3310                            line => $l, column => $c);
3311            $code = 0xFFFD;
3312          } elsif ($code == 0x000D) {
3313            !!!cp (1010);
3314            !!!parse-error (type => 'CR character reference', line => $l, column => $c);
3315            $code = 0x000A;
3316          } elsif (0x80 <= $code and $code <= 0x9F) {
3317            !!!cp (1011);
3318            !!!parse-error (type => 'C1 character reference', text => (sprintf 'U+%04X', $code), line => $l, column => $c);
3319            $code = $c1_entity_char->{$code};
3320          }
3321    
3322          if ($self->{prev_state} == DATA_STATE) {
3323            !!!cp (988);
3324            $self->{state} = $self->{prev_state};
3325            ## Reconsume.
3326            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3327                      line => $l, column => $c,
3328                     });
3329            redo A;
3330          } else {
3331            !!!cp (987);
3332            $self->{ca}->{value} .= chr $code;
3333            $self->{ca}->{has_reference} = 1;
3334            $self->{state} = $self->{prev_state};
3335            ## Reconsume.
3336            redo A;
3337          }
3338        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3339          if (length $self->{s_kwd} < 30 and
3340              ## NOTE: Some number greater than the maximum length of entity name
3341              ((0x0041 <= $self->{nc} and # a
3342                $self->{nc} <= 0x005A) or # x
3343               (0x0061 <= $self->{nc} and # a
3344                $self->{nc} <= 0x007A) or # z
3345               (0x0030 <= $self->{nc} and # 0
3346                $self->{nc} <= 0x0039) or # 9
3347               $self->{nc} == 0x003B)) { # ;
3348            our $EntityChar;
3349            $self->{s_kwd} .= chr $self->{nc};
3350            if (defined $EntityChar->{$self->{s_kwd}}) {
3351              if ($self->{nc} == 0x003B) { # ;
3352                !!!cp (1020);
3353                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3354                $self->{entity__match} = 1;
3355                !!!next-input-character;
3356                #
3357              } else {
3358                !!!cp (1021);
3359                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3360                $self->{entity__match} = -1;
3361                ## Stay in the state.
3362                !!!next-input-character;
3363                redo A;
3364              }
3365            } else {
3366              !!!cp (1022);
3367              $self->{entity__value} .= chr $self->{nc};
3368              $self->{entity__match} *= 2;
3369              ## Stay in the state.
3370            !!!next-input-character;            !!!next-input-character;
3371              redo A;
3372            }
3373          }
3374    
3375          my $data;
3376          my $has_ref;
3377          if ($self->{entity__match} > 0) {
3378            !!!cp (1023);
3379            $data = $self->{entity__value};
3380            $has_ref = 1;
3381            #
3382          } elsif ($self->{entity__match} < 0) {
3383            !!!parse-error (type => 'no refc');
3384            if ($self->{prev_state} != DATA_STATE and # in attribute
3385                $self->{entity__match} < -1) {
3386              !!!cp (1024);
3387              $data = '&' . $self->{s_kwd};
3388              #
3389            } else {
3390              !!!cp (1025);
3391              $data = $self->{entity__value};
3392              $has_ref = 1;
3393              #
3394          }          }
3395        } else {        } else {
3396          !!!cp (1022);          !!!cp (1026);
3397          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3398          $match *= 2;                          line => $self->{line_prev},
3399          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3400            $data = '&' . $self->{s_kwd};
3401            #
3402        }        }
3403      }    
3404              ## NOTE: In these cases, when a character reference is found,
3405      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3406        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3407        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3408                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3409               };        ## as string, either literal string when no character reference or
3410      } elsif ($match < 0) {        ## entity-replaced string otherwise, in this stage, since any characters
3411        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## that would not be consumed are appended in the data state or in an
3412        if ($in_attr and $match < -1) {        ## appropriate attribute value state anyway.
3413          !!!cp (1024);  
3414          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,        if ($self->{prev_state} == DATA_STATE) {
3415                  line => $l, column => $c,          !!!cp (986);
3416                 };          $self->{state} = $self->{prev_state};
3417        } else {          ## Reconsume.
3418          !!!cp (1025);          !!!emit ({type => CHARACTER_TOKEN,
3419          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,                    data => $data,
3420                  line => $l, column => $c,                    line => $self->{line_prev},
3421                 };                    column => $self->{column_prev} + 1 - length $self->{s_kwd},
3422                     });
3423            redo A;
3424          } else {
3425            !!!cp (985);
3426            $self->{ca}->{value} .= $data;
3427            $self->{ca}->{has_reference} = 1 if $has_ref;
3428            $self->{state} = $self->{prev_state};
3429            ## Reconsume.
3430            redo A;
3431        }        }
3432      } else {      } else {
3433        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero', line => $l, column => $c);  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value,  
               line => $l, column => $c,  
              };  
3434      }      }
3435    } else {    } # A  
3436      !!!cp (1027);  
3437      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3438      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3439    
3440  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3441    my $self = shift;    my $self = shift;
# Line 3169  sub _tree_construction_initial ($) { Line 3502  sub _tree_construction_initial ($) {
3502        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3503        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3504        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3505            defined $token->{system_identifier}) {            defined $token->{sysid}) {
3506          !!!cp ('t1');          !!!cp ('t1');
3507          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3508        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3509          !!!cp ('t2');          !!!cp ('t2');
3510          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3511        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3512          if ($token->{public_identifier} eq 'XSLT-compat') {          if ($token->{pubid} eq 'XSLT-compat') {
3513            !!!cp ('t1.2');            !!!cp ('t1.2');
3514            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
3515                            level => $self->{level}->{should});                            level => $self->{level}->{should});
# Line 3192  sub _tree_construction_initial ($) { Line 3525  sub _tree_construction_initial ($) {
3525          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3526        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
3527        ## are empty strings, so that we don't set any value in missing cases.        ## are empty strings, so that we don't set any value in missing cases.
3528        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3529            if defined $token->{public_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
       $doctype->system_id ($token->{system_identifier})  
           if defined $token->{system_identifier};  
3530        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3531        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3532        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 3203  sub _tree_construction_initial ($) { Line 3534  sub _tree_construction_initial ($) {
3534        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3535          !!!cp ('t4');          !!!cp ('t4');
3536          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3537        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3538          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3539          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3540          my $prefix = [          my $prefix = [
3541            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
# Line 3278  sub _tree_construction_initial ($) { Line 3609  sub _tree_construction_initial ($) {
3609            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3610          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3611                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3612            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3613              !!!cp ('t6');              !!!cp ('t6');
3614              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3615            } else {            } else {
# Line 3295  sub _tree_construction_initial ($) { Line 3626  sub _tree_construction_initial ($) {
3626        } else {        } else {
3627          !!!cp ('t10');          !!!cp ('t10');
3628        }        }
3629        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3630          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3631          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3632          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3633            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
# Line 4201  sub _tree_construction_main ($) { Line 4532  sub _tree_construction_main ($) {
4532            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4533              !!!cp ('t88.2');              !!!cp ('t88.2');
4534              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4535                #
4536            } else {            } else {
4537              !!!cp ('t88.1');              !!!cp ('t88.1');
4538              ## Ignore the token.              ## Ignore the token.
4539              !!!next-token;              #
             next B;  
4540            }            }
4541            unless (length $token->{data}) {            unless (length $token->{data}) {
4542              !!!cp ('t88');              !!!cp ('t88');
4543              !!!next-token;              !!!next-token;
4544              next B;              next B;
4545            }            }
4546    ## TODO: set $token->{column} appropriately
4547          }          }
4548    
4549          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
# Line 7456  sub _tree_construction_main ($) { Line 7788  sub _tree_construction_main ($) {
7788    ## TODO: script stuffs    ## TODO: script stuffs
7789  } # _tree_construct_main  } # _tree_construct_main
7790    
7791  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7792    my $class = shift;    my $class = shift;
7793    my $node = shift;    my $node = shift;
7794    my $s = \$_[0];    #my $s = \$_[0];
7795    my $onerror = $_[1];    my $onerror = $_[1];
7796      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7797    
7798    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7799    
# Line 7479  sub set_inner_html ($$$) { Line 7812  sub set_inner_html ($$$) {
7812      }      }
7813    
7814      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7815      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7816    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7817      ## TODO: If non-html element      ## TODO: If non-html element
7818    
7819      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7820    
7821    ## TODO: Support for $get_wrapper
7822    
7823      ## Step 1 # MUST      ## Step 1 # MUST
7824      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7825      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 7496  sub set_inner_html ($$$) { Line 7831  sub set_inner_html ($$$) {
7831      my $i = 0;      my $i = 0;
7832      $p->{line_prev} = $p->{line} = 1;      $p->{line_prev} = $p->{line} = 1;
7833      $p->{column_prev} = $p->{column} = 0;      $p->{column_prev} = $p->{column} = 0;
7834      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
7835        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7836        $input = $get_wrapper->($input);
7837        $p->{set_nc} = sub {
7838        my $self = shift;        my $self = shift;
7839    
7840        pop @{$self->{prev_char}};        my $char = '';
7841        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
7842            $char = $self->{next_nc};
7843        $self->{next_char} = -1 and return if $i >= length $$s;          delete $self->{next_nc};
7844        $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
7845          } else {
7846            $self->{char_buffer} = '';
7847            $self->{char_buffer_pos} = 0;
7848            
7849            my $count = $input->manakai_read_until
7850                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7851                 $self->{char_buffer_pos});
7852            if ($count) {
7853              $self->{line_prev} = $self->{line};
7854              $self->{column_prev} = $self->{column};
7855              $self->{column}++;
7856              $self->{nc}
7857                  = ord substr ($self->{char_buffer},
7858                                $self->{char_buffer_pos}++, 1);
7859              return;
7860            }
7861            
7862            if ($input->read ($char, 1)) {
7863              $self->{nc} = ord $char;
7864            } else {
7865              $self->{nc} = -1;
7866              return;
7867            }
7868          }
7869    
7870        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7871        $p->{column}++;        $p->{column}++;
7872    
7873        if ($self->{next_char} == 0x000A) { # LF        if ($self->{nc} == 0x000A) { # LF
7874          $p->{line}++;          $p->{line}++;
7875          $p->{column} = 0;          $p->{column} = 0;
7876          !!!cp ('i1');          !!!cp ('i1');
7877        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
7878          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
7879          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
7880            if ($input->read ($next, 1) and $next ne "\x0A") {
7881              $self->{next_nc} = $next;
7882            }
7883            $self->{nc} = 0x000A; # LF # MUST
7884          $p->{line}++;          $p->{line}++;
7885          $p->{column} = 0;          $p->{column} = 0;
7886          !!!cp ('i2');          !!!cp ('i2');
7887        } elsif ($self->{next_char} > 0x10FFFF) {        } elsif ($self->{nc} == 0x0000) { # NULL
         $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
         !!!cp ('i3');  
       } elsif ($self->{next_char} == 0x0000) { # NULL  
7888          !!!cp ('i4');          !!!cp ('i4');
7889          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7890          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
       } elsif ($self->{next_char} <= 0x0008 or  
                (0x000E <= $self->{next_char} and  
                 $self->{next_char} <= 0x001F) or  
                (0x007F <= $self->{next_char} and  
                 $self->{next_char} <= 0x009F) or  
                (0xD800 <= $self->{next_char} and  
                 $self->{next_char} <= 0xDFFF) or  
                (0xFDD0 <= $self->{next_char} and  
                 $self->{next_char} <= 0xFDDF) or  
                {  
                 0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
                 0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,  
                 0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,  
                 0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
                 0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,  
                 0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,  
                 0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,  
                 0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,  
                 0x10FFFE => 1, 0x10FFFF => 1,  
                }->{$self->{next_char}}) {  
         !!!cp ('i4.1');  
         if ($self->{next_char} < 0x10000) {  
           !!!parse-error (type => 'control char',  
                           text => (sprintf 'U+%04X', $self->{next_char}));  
         } else {  
           !!!parse-error (type => 'control char',  
                           text => (sprintf 'U-%08X', $self->{next_char}));  
         }  
7891        }        }
7892      };      };
7893      $p->{prev_char} = [-1, -1, -1];  
7894      $p->{next_char} = -1;      $p->{read_until} = sub {
7895              #my ($scalar, $specials_range, $offset) = @_;
7896          return 0 if defined $p->{next_nc};
7897    
7898          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7899          my $offset = $_[2] || 0;
7900          
7901          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7902            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7903            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7904              substr ($_[0], $offset)
7905                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7906              my $count = $+[0] - $-[0];
7907              if ($count) {
7908                $p->{column} += $count;
7909                $p->{char_buffer_pos} += $count;
7910                $p->{line_prev} = $p->{line};
7911                $p->{column_prev} = $p->{column} - 1;
7912                $p->{nc} = -1;
7913              }
7914              return $count;
7915            } else {
7916              return 0;
7917            }
7918          } else {
7919            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7920            if ($count) {
7921              $p->{column} += $count;
7922              $p->{column_prev} += $count;
7923              $p->{nc} = -1;
7924            }
7925            return $count;
7926          }
7927        }; # $p->{read_until}
7928    
7929      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7930        my (%opt) = @_;        my (%opt) = @_;
7931        my $line = $opt{line};        my $line = $opt{line};
# Line 7572  sub set_inner_html ($$$) { Line 7940  sub set_inner_html ($$$) {
7940        $ponerror->(line => $p->{line}, column => $p->{column}, @_);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7941      };      };
7942            
7943        my $char_onerror = sub {
7944          my (undef, $type, %opt) = @_;
7945          $ponerror->(layer => 'encode',
7946                      line => $p->{line}, column => $p->{column} + 1,
7947                      %opt, type => $type);
7948        }; # $char_onerror
7949        $input->onerror ($char_onerror);
7950    
7951      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7952      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
7953    

Legend:
Removed from v.1.160  
changed lines
  Added in v.1.184

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24