Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Monks, i have a list as shown below (sample) which is not a xml file. The file may have many nesting.

<input> <ordered-list> <item>some text <item>some text <item>some text <bulleted-list> <item>some text <item>some text <item>some text <end-list> <item>some text <item>some text <item>some text <end-list> </input>

I want to convert the above list into, as shown below.

<input> <ordered-list> <ordered-item>some text <ordered-item>some text <ordered-item>some text <bulleted-list> <bulleted-item>some text <bulleted-item>some text <bulleted-item>some text <end-list> <ordered-item>some text <ordered-item>some text <ordered-item>some text <end-list> </input>

I tried with repeated push and pop using if statement, is there any module to do this or is there any short way to achieve this?

Replies are listed 'Best First'.
Re: Nested list items
by Fletch (Bishop) on Sep 21, 2006 at 15:11 UTC

    You need to write a parser.

    state = [] ARGF.each do |line| state.push :input if line =~ /<input>/ state.push :ordered if line =~ /<ordered-list>/ state.push :bulleted if line =~ /<bulleted-list>/ state.pop if line =~ /<end-list>/ or line =~ /<\/input>/ if line =~ /<item>/ case state.last when :ordered line.gsub!( /<item>/, "<ordered-item>" ) when :bulleted line.gsub!( /<item>/, "<bulleted-item>" ) else ## do nothing end end puts line end

    Updated: Tweaked to actually run . . . and again to tweak state transitions to look prettier.

      Here's the Perl5 implementation of Fletch's code.

      my @state; while (<>) { push @state, 'input' if /<input>/; push @state, 'ordered' if /<ordered-list>/; push @state, 'bulleted' if /<bulleted-list>/; pop @state if /<end-list>/ || /<\/input>/; my $state = $state[$#state]; s/<item>/<ordered-item>/g if $state eq 'ordered'; s/<item>/<bulleted-item>/g if $state eq 'bulleted'; print; }

      Note that it has problems handling lines with multiple tags.

      Looks nice, but what the heck language is it?
Re: Nested list items
by davorg (Chancellor) on Sep 21, 2006 at 15:13 UTC

    Would be nice to see your code that doesn't work so we can advise you where you're going wrong, rather than just handing you an answer.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

Re: Nested list items
by ysth (Canon) on Sep 21, 2006 at 15:45 UTC
    push and pop sounds like the way to go to me; can you show what trouble you had? The code shouldn't be that long. It's unlikely that there'd be a module meeting these exact requirements.
Re: Nested list items (s///ge)
by tye (Sage) on Sep 21, 2006 at 16:36 UTC

    If you can't rely on having only one tag per line, then I'd probably go with something like this:

    my @list= "external"; s[(?<=<)([^>]+)(?=>))]{ my $tag= $1; if( $tag eq "item" ) { "$list[-1]-$tag"; } else { if( $tag eq "end-list" ) { pop @list if 1 < @list; } elsif( $tag =~ /(\w+)-list/ ) { push @list, $1; } $tag; } }ge;

    Note that you can still read the input line-by-line to avoid having to fit some entire huge file into memory at once (or set local $/= ">";); just put my first line outside of your read/write loop and the s///ge inside the read/write loop.

    - tye        

Re: Nested list items
by grep (Monsignor) on Sep 21, 2006 at 15:12 UTC
    Since you have not really given any code and you mention the use of push and pop, I assume you problem is with the data structure you are using (an array).

    I would use a HoH (hash of hashs) to store your structure.



    grep
    Mynd you, mønk bites Kan be pretti nasti...