+ return $t->type == TmplTokenType::DIRECTIVE? '%s':
+ $t->type == TmplTokenType::TEXT?
+ _formalize_string_cformat($t->string):
+ $t->type == TmplTokenType::TAG?
+ ($t->string =~ /^<a\b/is? '<a>':
+ $t->string =~ /^<input\b/is? (
+ lc $t->attributes->{'type'}->[1] eq 'text' ? '%S':
+ '%p'):
+ _quote_cformat($t->string)):
+ _quote_cformat($t->string);
+}
+
+sub _optimize {
+ my $this = shift;
+ my @structure = @_;
+ my $undo_trailing_blanks = sub {
+ for (my $i = $#structure; $i >= 0; $i -= 1) {
+ last unless ($structure[$i]->type == TmplTokenType::TEXT && blank_p($structure[$i]->string)) ;#|| ($structure[$i]->type == TmplTokenType::TAG && $structure[$i]->string =~ /^<br\b/is);
+ # Queue element structure: [reanalysis-p, token]
+ push @{$this->{_queue}}, [1, pop @structure];
+ }
+ };
+ &$undo_trailing_blanks;
+ while (@structure >= 2) {
+ my $something_done_p = 0;
+ # FIXME: If the last token is a close tag but there are no tags
+ # FIXME: before it, drop the close tag back into the queue. This
+ # FIXME: is an ugly hack to get rid of "foo %s</h1>" type mess.
+ if (@structure >= 2
+ && $structure[$#structure]->type == TmplTokenType::TAG
+ && $structure[$#structure]->string =~ /^<\//s) {
+ my $has_other_tags_p = 0;
+ for (my $i = 0; $i < $#structure; $i += 1) {
+ $has_other_tags_p = 1
+ if $structure[$i]->type == TmplTokenType::TAG;
+ last if $has_other_tags_p;
+ }
+ if (!$has_other_tags_p) {
+ push @{$this->{_queue}}, [0, pop @structure]
+ &$undo_trailing_blanks;
+ $something_done_p = 1;
+ }
+ }
+ # FIXME: Do the same ugly hack for the last token being a ( or [
+ if (@structure >= 2
+ && $structure[$#structure]->type == TmplTokenType::TEXT
+ && $structure[$#structure]->string =~ /^[\(\[]$/) { # not )]
+ push @{$this->{_queue}}, [1, pop @structure];
+ &$undo_trailing_blanks;
+ $something_done_p = 1;
+ }
+ # FIXME: If the first token is an open tag, but there is no
+ # FIXME: corresponding close tag, "drop the open tag", i.e.,
+ # FIXME: requeue everything for reanalysis, except the frist tag. :-(
+ if (@structure >= 2
+ && $structure[0]->type == TmplTokenType::TAG
+ && $structure[0]->string =~ /^<([a-z0-9]+)/is
+ && (my $tag = $1) !~ /^(?:br|hr|img|input)\b/is
+ ) {
+ my $tag_open_count = 1;
+ for (my $i = 1; $i <= $#structure; $i += 1) {
+ if ($structure[$i]->type == TmplTokenType::TAG) {
+ if ($structure[$i]->string =~ /^<(\/?)$tag\b/is) {
+ $tag_open_count += ($1? -1: +1);
+ }
+ }
+ }
+ if ($tag_open_count > 0) {
+ for (my $i = $#structure; $i; $i -= 1) {
+ push @{$this->{_queue}}, [1, pop @structure];
+ }
+ $something_done_p = 1;
+ }
+ }
+ # FIXME: If the first token is an open tag, the last token is the
+ # FIXME: corresponding close tag, and there are no other close tags
+ # FIXME: inbetween, requeue the tokens from the second token on,
+ # FIXME: flagged as ok for re-analysis
+ if (@structure >= 3
+ && $structure[0]->type == TmplTokenType::TAG
+ && $structure[0]->string =~ /^<([a-z0-9]+)/is && (my $tag = $1)
+ && $structure[$#structure]->type == TmplTokenType::TAG
+ && $structure[$#structure]->string =~ /^<\/$1\s*>$/is) {
+ my $has_other_open_or_close_tags_p = 0;
+ for (my $i = 1; $i < $#structure; $i += 1) {
+ $has_other_open_or_close_tags_p = 1
+ if $structure[$i]->type == TmplTokenType::TAG
+ && $structure[$i]->string =~ /^<\/?$tag\b/is;
+ last if $has_other_open_or_close_tags_p;
+ }
+ if (!$has_other_open_or_close_tags_p) {
+ for (my $i = $#structure; $i; $i -= 1) {
+ push @{$this->{_queue}}, [1, pop @structure];
+ }
+ $something_done_p = 1;
+ }
+ }
+ last if !$something_done_p;
+ }
+ return @structure;
+}
+
+sub looks_plausibly_like_groupable_text_p (@) {
+ my @structure = @_;
+ # The text would look plausibly groupable if all open tags are also closed.
+ my @tags = ();
+ my $error_p = 0;
+ for (my $i = 0; $i <= $#structure; $i += 1) {
+ if ($structure[$i]->type == TmplTokenType::TAG) {
+ my $form = $structure[$i]->string;
+ if ($form =~ /^<([A-Z0-9]+)/is) {
+ my $tag = lc($1);
+ if ($tag !~ /^(?:br|input)$/is && $form !~ /\/>$/is) {
+ push @tags, $tag;
+ }
+ } elsif ($form =~ /^<\/([A-Z0-9]+)/is) {
+ if (@tags && lc($1) eq $tags[$#tags]) {
+ pop @tags;
+ } else {
+ $error_p = 1;
+ }
+ }
+ } elsif ($structure[$i]->type != TmplTokenType::TEXT) {
+ $error_p = 1;
+ }
+ last if $error_p;
+ }
+ return !$error_p && !@tags;