Dear hdb and ig, I know it is over a year since you did so much work to help me get my data to look the way I wanted it to look. About a month ago, I finally sat down with your work, and forced myself to get it incorporated come hell or high water. Below is the result of your hard work with very little done by me except to do some minor modifications in expanding the scope.

hdb, I used your work in Re: Need help with filling a complicated data structure to make my lists.

ig, I used your work in Re^3: Need help with filling a complicated data structure to group the lines together for my lists and tables. I also used it when it came time to parse the tables. Don't worry, I didn't include tables in my original post. I had not used for like you did before now. I had never taken it apart to understand it until your work made me look into it. So thank you for the push I needed to learn something new.

So, at long last, here is the code which effectively solved my problem with your help. I am so very thankful for your work, sorry it comes so late.

# Begin subroutines used in 'story' below. # 'convert_string' finds ^words between carrots^ and matches them to k +eys in $line_magic # and does replacements. sub convert_string { my ($string, $line_magic) = @_; $string =~ s/\^([\w\s\.\#]+)\^/exists $line_magic->{$1} ? $line_magi +c->{$1} : $1/ge; return $string; } # 'row_line' returns the array ref of a row of cells which I use in 't +able_opts'. sub row_line { my ($line, $opt) = @_; my @row = split(/\|/, $line); my $row_data; for my $cell (@row) { push @{$row_data}, $cell =~ /^r(\d)\s(.+)$/ ? [inline(convert_stri +ng($2, $opt->{'line magic'})), { 'rowspan' => $1 }] : $cell =~ /^c(\d)\s(.+)$/ ? [inline(convert_stri +ng($2, $opt->{'line magic'})), { 'colspan' => $1 }] : inline(convert_string($cell, $opt->{'line magic +'})); } return $row_data; } # 'table_opts' returns all of the options for my separate 'table' func +tion (not included here). # In 'story' below, I group all lines beginning with a | and them pars +e them here. # [ig] showed me how to group lines together in a loop which I used he +re and in 'story'. # || the_tables_class # |! The table's caption # |* The|table's|headings # |+ A|row|which|starts|with|a|heading # |- A|now|which|doesn't start|with|a|heading sub table_opts { my ($lines, $opt) = @_; my $table_opts; my $match = '|!*+-'; for (my $lineno = 0; $lineno < @$lines; $lineno++) { my $line = $lines->[$lineno]; $match = substr($line, 0, 1); if ($match eq '|') { $line =~ s/^\| (.+)$/$1/; $table_opts->{'class'} = $line; } elsif ($match eq '!') { $line =~ s/^\! (.+)$/$1/; $table_opts->{'caption'} = inline(convert_string($line), $opt->{ +'line magic'}); } elsif ($match eq '*') { $line =~ s/^\* (.+)$/$1/; push @{$table_opts->{'rows'}}, ['header', row_line($line, $opt)] +; } elsif ($match =~ /[\+-]/) { my $start = $lineno; my $end = $lineno; $end++ while ($end < $#$lines and $lines->[$end + 1] =~ /^[$matc +h]/); my @table_rows = map { $_ =~ s/^[\+-] (.+)/$1/; row_line($_, $opt); } @{$lines}[$start..$end]; my $type = $match =~ /\+/ ? 'whead' : 'data'; push @{$table_opts->{'rows'}}, [$type, \@table_rows]; $lineno = $end; } $match = '*+-'; } return $table_opts; } # 'type' and 'get_list' are written by [hdb]. [ig] wrote a version too +. # 'get_list' parses the groups of lines which begin with a * or a # in + my data. # The returned list is plugged into my 'list' function which is not in +cluded here. sub type { my %type = ( '*' => 'u', '#' => 'o' ); $type{ substr shift, -1 } } sub get_list { my ($list, $opt) = @_; $list = [ map (inline(convert_string($_, $opt->{'line magic'})), + @$list) ]; my @lines = map { /([*#]*)(\d*)\s+(.*)/; $2 ? [$1, [$3, {value => $2}]] : [$1, $3] } @$list; my $maxlevel = max map { length $_->[0] } @lines; while( $maxlevel ) { my @indices = grep { $maxlevel == length $lines[$_]->[0] } 0..@lin +es - 1; while( @indices ) { my $end = pop @indices; my $start = $end; $start = pop @indices while @indices and $indices[-1] == $start- +1; my $sublist = [ type($lines[$start]->[0]), [ map { $_->[1] } splice @lines, $start, $end-$start + 1 ] ]; $lines[$start-1]->[1] = [ $lines[$start-1]->[1], { 'inlist' => $ +sublist} ] if $maxlevel > 1; splice @lines, $start, 0, $sublist if $maxlevel == 1; } $maxlevel--; } @lines = grep { $_->[0] } @lines; } # 'heading_w_links' is something I wrote earlier. # It adds links to wikipedia and google under the heading of a section +. sub heading_w_links { my ($tab, $level, $text) = @_; my ($heading, $wikipedia) = split(/\|/,$text); my $article = $wikipedia ? $wikipedia : $heading; heading($tab, $level, textify($heading), { id => idify($heading) }); paragraph($tab + 1, external_links([['Wikipedia',filify($article)],[ +'Google',searchify($heading)]]), { 'style' => 'float: right' } ); } # End subroutines used in 'story' below. sub story { my ($source, $opt) = @_; # Start table of contents and sections. my $inc = 0; my $cols = 0; my @sections; my @toc; while (my $line = <$source>) { chomp($line); next if !$line; if ($line =~ /^2 /) { my ($number,$text) = split(/ /,$line,2); $text =~ s/ \+$//; push @toc, [anchor(textify($text), { href => '#'.idify($text) }) +]; } if ($line =~ /^3 /) { my ($number,$text) = split(/ /,$line,2); $text =~ s/ \+$//; $toc[$inc-1][1]->{inlist}[0] = 'u'; push @{$toc[$inc-1][1]->{inlist}[1]}, anchor(textify($text), { h +ref => '#'.idify($text) }); #$toc[$inc-1][1]->{inlist}[2]{'style'} = 'font-size: smaller;'; } $inc++ if $line =~ /^2 /; $cols++ if $line =~ /^(?:2|3) /; push @{$sections[$inc]}, $line; } # End table of contents and sections. # Start parsing the sections. my $tab = 2; my $match = '\*#|'; # These characters denote lists or tables. $inc = 0; for my $section (@sections) { if ($section) { section($tab, sub { $tab++; # I'd never used for like the following before now. for (my $lineno = 0; $lineno < @$section; $lineno++) { my $line = $section->[$lineno]; if ($line =~ /^[$match]/) { # $match is shortened to one character for future lines +to match. # It also tells the parser where to send the grouped lin +es, # table (|) or list (* or #). $match = substr($line, 0, 1); my $start = $lineno; my $end = $lineno; $end++ while ($end < $#$section and $section->[$end+1] = +~ /^[$match]/); my @list_lines = @{$section}[$start..$end]; # lines beginning with | all belong to a table and are s +ent to 'table_opt'. if ($match eq '|') { my @list = map { $_ =~ s/^\|(.+)/$1/; $_; } @list_line +s; my $opts = table_opts(\@list, $opt); table($tab + 1, $opts); } # lines beginning with a * or # belong to a list and are + sent to 'get_list'. else { my $class = $list_lines[0] =~ s/^[\*#]\| (.+)$/$1/ ? s +hift @list_lines : undef; my @list = get_list(\@list_lines, $opt); $list[0][2]->{'class'} = $class ? $class : undef; list($tab + 1, @{$list[0]}); } $lineno = $end; # Reset $match after a set of lines is grouped, ready fo +r next grouping. $match = '\*#|'; } else { heading_w_links($tab, $1, $2), next if $line =~ /^([1-6] +)\s+(.+) \+$/; heading($tab, $1, textify($2), { id => idify($2) }), nex +t if $line =~ /^([1-6])\s+(.+)/; div($tab, $2, { 'class' => "h$1" }), next if $line =~ /^ +([7-8])\s+(.+)/; $opt->{'doc magic'}->{$1}->(), next if $line =~ /^&\s+(. ++)/; line($tab + 1, $line), next if $line =~ /^<.+>/; line($tab + 1, "<$line>"), next if $line =~ /^[bh]r$/; blockquote($tab + 1, inline(convert_string($1, $opt->{'l +ine magic'}))), next if $line =~ /^bq\s(.+)/; # paragraphs paragraph($tab + 1, inline(convert_string($1, $opt->{'li +ne magic'})), { class => 'stanza', break => '\|' }), next if $line =~ + /^stanza (.+)$/; paragraph($tab + 1, inline(convert_string($2, $opt->{'li +ne magic'})), { class => "indent$1"}), next if $line =~ /^\>(\d+) ( +.+)$/; paragraph($tab + 1, inline(convert_string($line, $opt->{ +'line magic'})), { class => 'author' }), next if $line =~ /^(?:by|wit +h|from|as) /; paragraph($tab + 1, inline(convert_string($line, $opt->{ +'line magic'}))); } } $tab--; }); } my $toc_start = $opt->{'toc at'} ? $opt->{'toc at'} : 3; if ($inc == 0 && $cols >= $toc_start) { nav($tab, sub { my $class = get_columns(4, $cols); list($tab + 2, 'u', \@toc, { class => $class }); }, { heading => [2, 'Table of contents'], class => 'contents'} ) +; } $inc++; } # End parsing sections. }

hdb and ig, you helped me a lot! Thank you again! (I just hope my comments are clear enough, and my code makes sense.)

No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
Lady Aleena

In reply to Re: Need help with filling a complicated data structure: Epilog by Lady_Aleena
in thread Need help with filling a complicated data structure by Lady_Aleena

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.