/[suikacvs]/markup/html/whatpm/Whatpm/CSS/Parser.pm
Suika

Diff of /markup/html/whatpm/Whatpm/CSS/Parser.pm

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

revision 1.4 by wakaba, Sun Dec 23 15:47:09 2007 UTC revision 1.7 by wakaba, Mon Dec 31 09:09:23 2007 UTC
# Line 5  require Whatpm::CSS::SelectorsParser; Line 5  require Whatpm::CSS::SelectorsParser;
5    
6  sub new ($) {  sub new ($) {
7    my $self = bless {onerror => sub { }, must_level => 'm',    my $self = bless {onerror => sub { }, must_level => 'm',
8                        message_level => 'w',
9                      unsupported_level => 'unsupported'}, shift;                      unsupported_level => 'unsupported'}, shift;
10    
11    return $self;    return $self;
# Line 15  sub BEFORE_DECLARATION_STATE () { 1 } Line 16  sub BEFORE_DECLARATION_STATE () { 1 }
16  sub IGNORED_STATEMENT_STATE () { 2 }  sub IGNORED_STATEMENT_STATE () { 2 }
17  sub IGNORED_DECLARATION_STATE () { 3 }  sub IGNORED_DECLARATION_STATE () { 3 }
18    
19    our $Prop; ## By CSS property name
20    our $Attr; ## By CSSOM attribute name
21    our $Key; ## By internal key
22    
23  sub parse_char_string ($$) {  sub parse_char_string ($$) {
24    my $self = $_[0];    my $self = $_[0];
25    
# Line 88  sub parse_char_string ($$) { Line 93  sub parse_char_string ($$) {
93                $t->{type} == CDC_TOKEN;                $t->{type} == CDC_TOKEN;
94    
95        if ($t->{type} == ATKEYWORD_TOKEN) {        if ($t->{type} == ATKEYWORD_TOKEN) {
96          if ($t->{value} eq 'namespace') {          if (lc $t->{value} eq 'namespace') { ## TODO: case folding
97            $t = $tt->get_next_token;            $t = $tt->get_next_token;
98            $t = $tt->get_next_token while $t->{type} == S_TOKEN;            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
99    
# Line 138  sub parse_char_string ($$) { Line 143  sub parse_char_string ($$) {
143                       level => $self->{must_level},                       level => $self->{must_level},
144                       token => $t);                       token => $t);
145            #            #
146          } elsif ($t->{value} eq 'charset') {          } elsif (lc $t->{value} eq 'charset') { ## TODO: case folding
147            $t = $tt->get_next_token;            $t = $tt->get_next_token;
148            $t = $tt->get_next_token while $t->{type} == S_TOKEN;            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
149    
# Line 233  sub parse_char_string ($$) { Line 238  sub parse_char_string ($$) {
238        ## NOTE: DELIM? in declaration will be removed:        ## NOTE: DELIM? in declaration will be removed:
239        ## <http://csswg.inkedblade.net/spec/css2.1?s=declaration%20delim#issue-2>.        ## <http://csswg.inkedblade.net/spec/css2.1?s=declaration%20delim#issue-2>.
240    
241          my $prop_def;
242          my $prop_value;
243          my $prop_flag;
244        $t = $tt->get_next_token while $t->{type} == S_TOKEN;        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
245        if ($t->{type} == IDENT_TOKEN) { # property        if ($t->{type} == IDENT_TOKEN) { # property
246          ## TODO: If supported, ...          my $prop_name = lc $t->{value}; ## TODO: case folding
   
247          $t = $tt->get_next_token;          $t = $tt->get_next_token;
248          #          if ($t->{type} == COLON_TOKEN) {
249        } elsif ($t->{type} == RBRACE_TOKEN) {            $t = $tt->get_next_token;
250              $t = $tt->get_next_token while $t->{type} == S_TOKEN;
251    
252              $prop_def = $Prop->{$prop_name};
253              if ($prop_def and $self->{prop}->{$prop_name}) {
254                ($t, $prop_value)
255                    = $prop_def->{parse}->($self, $prop_name, $tt, $t, $onerror);
256                if ($prop_value) {
257                  ## NOTE: {parse} don't have to consume trailing spaces.
258                  $t = $tt->get_next_token while $t->{type} == S_TOKEN;
259    
260                  if ($t->{type} == EXCLAMATION_TOKEN) {
261                    $t = $tt->get_next_token;
262                    $t = $tt->get_next_token while $t->{type} == S_TOKEN;
263                    if ($t->{type} == IDENT_TOKEN and
264                        lc $t->{value} eq 'important') { ## TODO: case folding
265                      $prop_flag = 'important';
266                      
267                      $t = $tt->get_next_token;
268                      $t = $tt->get_next_token while $t->{type} == S_TOKEN;
269    
270                      #
271                    } else {
272                      $onerror->(type => 'syntax error:important',
273                                 level => $self->{must_level},
274                                 token => $t);
275                      
276                      ## Reprocess.
277                      $state = IGNORED_DECLARATION_STATE;
278                      redo S;
279                    }
280                  }
281    
282                  #
283                } else {
284                  ## Syntax error.
285            
286                  ## Reprocess.
287                  $state = IGNORED_DECLARATION_STATE;
288                  redo S;
289                }
290              } else {
291                $onerror->(type => 'not supported:property',
292                           level => $self->{unsupported_level},
293                           token => $t, value => $prop_name);
294    
295                #
296                $state = IGNORED_DECLARATION_STATE;
297                redo S;
298              }
299            } else {
300              $onerror->(type => 'syntax error:property colon',
301                         level => $self->{must_level},
302                         token => $t);
303    
304              #
305              $state = IGNORED_DECLARATION_STATE;
306              redo S;
307            }
308          }
309    
310          if ($t->{type} == RBRACE_TOKEN) {
311          $t = $tt->get_next_token;          $t = $tt->get_next_token;
312          $state = BEFORE_STATEMENT_STATE;          $state = BEFORE_STATEMENT_STATE;
313          redo S;          #redo S;
314          } elsif ($t->{type} == SEMICOLON_TOKEN) {
315            $t = $tt->get_next_token;
316            ## Stay in the state.
317            #redo S;
318        } elsif ($t->{type} == EOF_TOKEN) {        } elsif ($t->{type} == EOF_TOKEN) {
319          $onerror->(type => 'syntax error:ruleset not closed',          $onerror->(type => 'syntax error:ruleset not closed',
320                     level => $self->{must_level},                     level => $self->{must_level},
321                     token => $t);                     token => $t);
322          ## Reprocess.          ## Reprocess.
323          $state = BEFORE_STATEMENT_STATE;          $state = BEFORE_STATEMENT_STATE;
324            #redo S;
325          } else {
326            if ($prop_value) {
327              $onerror->(type => 'syntax error:property semicolon',
328                         level => $self->{must_level},
329                         token => $t);
330            } else {
331              $onerror->(type => 'syntax error:property name',
332                         level => $self->{must_level},
333                         token => $t);
334            }
335    
336            #
337            $state = IGNORED_DECLARATION_STATE;
338          redo S;          redo S;
339        }        }
340    
341        #        my $important = (defined $prop_flag and $prop_flag eq 'important');
342        $state = IGNORED_DECLARATION_STATE;        for my $set_prop_name (keys %{$prop_value or {}}) {
343            my $set_prop_def = $Prop->{$set_prop_name};
344            $$current_decls->{$set_prop_def->{key}}
345                = [$prop_value->{$set_prop_name}, $prop_flag]
346                if $important or
347                    not $$current_decls->{$set_prop_def->{key}} or
348                    not defined $$current_decls->{$set_prop_def->{key}}->[1];
349          }
350        redo S;        redo S;
351      } elsif ($state == IGNORED_STATEMENT_STATE or      } elsif ($state == IGNORED_STATEMENT_STATE or
352               $state == IGNORED_DECLARATION_STATE) {               $state == IGNORED_DECLARATION_STATE) {
# Line 337  sub parse_char_string ($$) { Line 430  sub parse_char_string ($$) {
430    return $ss;    return $ss;
431  } # parse_char_string  } # parse_char_string
432    
433    $Prop->{color} = {
434      css => 'color',
435      dom => 'color',
436      key => 'color',
437      parse => sub {
438        my ($self, $prop_name, $tt, $t, $onerror) = @_;
439    
440        if ($t->{type} == IDENT_TOKEN) {
441          if (lc $t->{value} eq 'blue') { ## TODO: case folding
442            $t = $tt->get_next_token;
443            return ($t, {$prop_name => ["RGBA", 0, 0, 255, 1]});
444          } else {
445            #
446          }
447        } else {
448          #
449        }
450    
451        $onerror->(type => 'syntax error:color',
452                   level => $self->{must_level},
453                   token => $t);
454        
455        return ($t, undef);
456      },
457      serialize => sub {
458        my ($self, $prop_name, $value) = @_;
459        if ($value->[0] eq 'RGBA') { ## TODO: %d? %f?
460          return sprintf 'rgba(%d, %d, %d, %f)', @$value[1, 2, 3, 4];
461        } else {
462          return undef;
463        }
464      },
465    };
466    $Attr->{color} = $Prop->{color};
467    $Key->{color} = $Prop->{color};
468    
469    my $one_keyword_parser = sub {
470      my ($self, $prop_name, $tt, $t, $onerror) = @_;
471    
472      if ($t->{type} == IDENT_TOKEN) {
473        my $prop_value = lc $t->{value}; ## TODO: case folding
474        $t = $tt->get_next_token;
475        if ($Prop->{$prop_name}->{keyword}->{$prop_value} and
476            $self->{prop_value}->{$prop_name}->{$prop_value}) {
477          return ($t, {$prop_name => ["KEYWORD", $prop_value]});
478        } elsif ($prop_value eq 'inherit') {
479          return ($t, {$prop_name => ["KEYWORD", $prop_value]});
480        }
481      }
482      
483      $onerror->(type => 'syntax error:keyword:'.$prop_name,
484                 level => $self->{must_level},
485                 token => $t);
486      return ($t, undef);
487    };
488    
489    my $one_keyword_serializer = sub {
490      my ($self, $prop_name, $value) = @_;
491      if ($value->[0] eq 'KEYWORD') {
492        return $value->[1];
493      } else {
494        return undef;
495      }
496    };
497    
498    $Prop->{display} = {
499      css => 'display',
500      dom => 'display',
501      key => 'display',
502      parse => $one_keyword_parser,
503      serialize => $one_keyword_serializer,
504      keyword => {
505        block => 1, inline => 1, 'inline-block' => 1, 'inline-table' => 1,
506        'list-item' => 1, none => 1,
507        table => 1, 'table-caption' => 1, 'table-cell' => 1, 'table-column' => 1,
508        'table-column-group' => 1, 'table-header-group' => 1,
509        'table-footer-group' => 1, 'table-row' => 1, 'table-row-group' => 1,
510      },
511    };
512    $Attr->{display} = $Prop->{display};
513    $Key->{display} = $Prop->{display};
514    
515    $Prop->{position} = {
516      css => 'position',
517      dom => 'position',
518      key => 'position',
519      parse => $one_keyword_parser,
520      serialize => $one_keyword_serializer,
521      keyword => {
522        static => 1, relative => 1, absolute => 1, fixed => 1,
523      },
524    };
525    $Attr->{position} = $Prop->{position};
526    $Key->{position} = $Prop->{position};
527    
528    $Prop->{float} = {
529      css => 'float',
530      dom => 'css_float',
531      key => 'float',
532      parse => $one_keyword_parser,
533      serialize => $one_keyword_serializer,
534      keyword => {
535        left => 1, right => 1, none => 1,
536      },
537    };
538    $Attr->{css_float} = $Prop->{float};
539    $Attr->{style_float} = $Prop->{float}; ## NOTE: IEism
540    $Key->{float} = $Prop->{float};
541    
542    $Prop->{clear} = {
543      css => 'clear',
544      dom => 'clear',
545      key => 'clear',
546      parse => $one_keyword_parser,
547      serialize => $one_keyword_serializer,
548      keyword => {
549        left => 1, right => 1, none => 1, both => 1,
550      },
551    };
552    $Attr->{clear} = $Prop->{clear};
553    $Key->{clear} = $Prop->{clear};
554    
555    $Prop->{direction} = {
556      css => 'direction',
557      dom => 'direction',
558      key => 'direction',
559      parse => $one_keyword_parser,
560      serialize => $one_keyword_serializer,
561      keyword => {
562        ltr => 1, rtl => 1,
563      },
564    };
565    $Attr->{direction} = $Prop->{direction};
566    $Key->{direction} = $Prop->{direction};
567    
568    $Prop->{'unicode-bidi'} = {
569      css => 'unicode-bidi',
570      dom => 'unicode_bidi',
571      key => 'unicode_bidi',
572      parse => $one_keyword_parser,
573      serialize => $one_keyword_serializer,
574      keyword => {
575        normal => 1, embed => 1, 'bidi-override' => 1,
576      },
577    };
578    $Attr->{unicode_bidi} = $Prop->{'unicode-bidi'};
579    $Key->{unicode_bidi} = $Prop->{'unicode-bidi'};
580    
581    my $border_style_keyword = {
582      none => 1, hidden => 1, dotted => 1, dashed => 1, solid => 1,
583      double => 1, groove => 1, ridge => 1, inset => 1, outset => 1,
584    };
585    
586    $Prop->{'border-top-style'} = {
587      css => 'border-top-style',
588      dom => 'border_top_style',
589      key => 'border_top_style',
590      parse => $one_keyword_parser,
591      serialize => $one_keyword_serializer,
592      keyword => $border_style_keyword,
593    };
594    $Attr->{border_top_style} = $Prop->{'border-top-style'};
595    $Key->{border_top_style} = $Prop->{'border-top-style'};
596    
597    $Prop->{'border-right-style'} = {
598      css => 'border-right-style',
599      dom => 'border_right_style',
600      key => 'border_right_style',
601      parse => $one_keyword_parser,
602      serialize => $one_keyword_serializer,
603      keyword => $border_style_keyword,
604    };
605    $Attr->{border_right_style} = $Prop->{'border-right-style'};
606    $Key->{border_right_style} = $Prop->{'border-right-style'};
607    
608    $Prop->{'border-bottom-style'} = {
609      css => 'border-bottom-style',
610      dom => 'border_bottom_style',
611      key => 'border_bottom_style',
612      parse => $one_keyword_parser,
613      serialize => $one_keyword_serializer,
614      keyword => $border_style_keyword,
615    };
616    $Attr->{border_bottom_style} = $Prop->{'border-bottom-style'};
617    $Key->{border_bottom_style} = $Prop->{'border-bottom-style'};
618    
619    $Prop->{'border-left-style'} = {
620      css => 'border-left-style',
621      dom => 'border_left_style',
622      key => 'border_left_style',
623      parse => $one_keyword_parser,
624      serialize => $one_keyword_serializer,
625      keyword => $border_style_keyword,
626    };
627    $Attr->{border_left_style} = $Prop->{'border-left-style'};
628    $Key->{border_left_style} = $Prop->{'border-left-style'};
629    
630    $Prop->{'border-style'} = {
631      css => 'border-style',
632      dom => 'border_style',
633      parse => sub {
634        my ($self, $prop_name, $tt, $t, $onerror) = @_;
635    
636        my %prop_value;
637        my $has_inherit;
638        if ($t->{type} == IDENT_TOKEN) {
639          my $prop_value = lc $t->{value}; ## TODO: case folding
640          $t = $tt->get_next_token;
641          if ($border_style_keyword->{$prop_value} and
642              $self->{prop_value}->{'border-top-style'}->{$prop_value}) {
643            $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];
644          } elsif ($prop_value eq 'inherit') {
645            $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];
646            $has_inherit = 1;
647          } else {
648            $onerror->(type => 'syntax error:keyword:'.$prop_name,
649                       level => $self->{must_level},
650                       token => $t);
651            return ($t, undef);
652          }
653          $prop_value{'border-right-style'} = $prop_value{'border-top-style'};
654          $prop_value{'border-bottom-style'} = $prop_value{'border-top-style'};
655          $prop_value{'border-left-style'} = $prop_value{'border-right-style'};
656        } else {
657          $onerror->(type => 'syntax error:keyword:'.$prop_name,
658                     level => $self->{must_level},
659                     token => $t);
660          return ($t, undef);
661        }
662    
663        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
664        if ($t->{type} == IDENT_TOKEN) {
665          my $prop_value = lc $t->{value}; ## TODO: case folding
666          $t = $tt->get_next_token;
667          if (not $has_inherit and
668              $border_style_keyword->{$prop_value} and
669              $self->{prop_value}->{'border-right-style'}->{$prop_value}) {
670            $prop_value{'border-right-style'} = ["KEYWORD", $prop_value];
671          } else {
672            $onerror->(type => 'syntax error:keyword:'.$prop_name,
673                       level => $self->{must_level},
674                       token => $t);
675            return ($t, undef);
676          }
677          $prop_value{'border-left-style'} = $prop_value{'border-right-style'};
678    
679          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
680          if ($t->{type} == IDENT_TOKEN) {
681            my $prop_value = lc $t->{value}; ## TODO: case folding
682            $t = $tt->get_next_token;
683            if ($border_style_keyword->{$prop_value} and
684                $self->{prop_value}->{'border-bottom-style'}->{$prop_value}) {
685              $prop_value{'border-bottom-style'} = ["KEYWORD", $prop_value];
686            } else {
687              $onerror->(type => 'syntax error:keyword:'.$prop_name,
688                         level => $self->{must_level},
689                         token => $t);
690              return ($t, undef);
691            }
692            
693            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
694            if ($t->{type} == IDENT_TOKEN) {
695              my $prop_value = lc $t->{value}; ## TODO: case folding
696              $t = $tt->get_next_token;
697              if ($border_style_keyword->{$prop_value} and
698                  $self->{prop_value}->{'border-left-style'}->{$prop_value}) {
699                $prop_value{'border-left-style'} = ["KEYWORD", $prop_value];
700              } else {
701                $onerror->(type => 'syntax error:keyword:'.$prop_name,
702                           level => $self->{must_level},
703                           token => $t);
704                return ($t, undef);
705              }
706            }
707          }
708        }        
709    
710        return ($t, \%prop_value);
711      },
712      serialize => sub {
713        my ($self, $prop_name, $value) = @_;
714        
715        local $Error::Depth = $Error::Depth + 1;
716        my @v;
717        push @v, $self->border_top_style;
718        return undef unless defined $v[-1];
719        push @v, $self->border_right_style;
720        return undef unless defined $v[-1];
721        push @v, $self->border_bottom_style;
722        return undef unless defined $v[-1];
723        push @v, $self->border_bottom_style;
724        return undef unless defined $v[-1];
725    
726        pop @v if $v[1] eq $v[3];
727        pop @v if $v[0] eq $v[2];
728        pop @v if $v[0] eq $v[1];
729        return join ' ', @v;
730      },
731    };
732    $Attr->{border_style} = $Prop->{'border-style'};
733    
734  1;  1;
735  ## $Date$  ## $Date$

Legend:
Removed from v.1.4  
changed lines
  Added in v.1.7

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24