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

I am trying to write some XML::Twig code which transforms nested elements into arithmetical expressions, e.g.:
<set> $z <add>$x $y</add> </set>
turns into
$z = $x + $y
So in order for my TwigHandlers to fire on the <add> nested inside the <set>, I recursively create a new XML::Twig and parse the small <add> element:
{ my $twig = new XML::Twig ( %constructor ) ; $twig->parse($elt->next_elt->sprint); }
and this does in fact work: my TwigHandlers notice the <add> tag and fire. However I have two different behaviors I want from my handlers. Sometimes I want them to print their output and other times I want them to append to a string.

this is controlled by a package variable called $pmode. When it is true, then the call to my print routine will print, otherwise it will append to a string. I am altering $pmode by the use of local(), but for some reason, when the TwigHandler for add fires and $pmode has been localized, it instead has the non-localized true version, when it should have the localized false version.

I print $pmode immediately before and after the call to parse in the newly created twig, and it is false in both cases, however when the TwigHandler fires and I print $pmode there, unfortunately it is true... but how this could be is confusing.

#!/usr/local/bin/perl use Data::Dumper; use XML::Twig; ++$|; our $pmode = 1; my $pstring; sub pt { my (@string) = @_; for (@string) { if ($pmode) { print $_; } else { $pstring .= $_; } } } # print expression - no semicolon at end sub pe { pt $_[0] } # print statement - semicolon at end sub ps { pt "$_[0];\n"; } # print statement demux - distribute 1 fish to each of the masses sub ps_dmux { my ($fish, @masses) = @_; map { ps "$fish $_" } @masses; } # print statement with masses as a parenthesized arguement sub ps_pare { my ($fish, @masses) = @_; ps sprintf "$fish (%s)", join ',', @masses; } my %constructor = ( StartTagHandlers => { scope => sub { print "{" }, perl => sub { my $T = $_[1]->att("binary"); printf "#!%s\n", $T }, }, TwigHandlers => { scope => sub { print "}" }, use => sub { my @p = split /\s+/, $_[1]->text; ps_dmux $_[1]->gi +, @p }, my => sub { my @p = split /\s+/, $_[1]->text; ps_pare $_[1]->gi +, @p }, print => sub { my @p = split /\s+/, $_[1]->text; ps_pare $_[1]->gi +, @p }, add => sub { my @p = split /\s+/, $_[1]->text; warn " [add pmode +: $pmode]\ "; pe join '+', @p }, set => sub { my $elt = $_[1]; my @children = $elt->children; if (@children == 1) { ps sprintf "%s = %s", split /\s+/, $elt->text } else { # children == 2 or more { local $pmode; pt $elt->next_elt->text, " = "; $elt = $elt->next_elt; warn " [else pmode : $pmode ] "; my $twig = new XML::Twig ( %constructor ) ; $twig->parse($elt->next_elt->sprint); warn " [else pmode 2 : $pmode ] "; } warn " [ non-local pmode: $pmode ]"; pt $pstring; $pstring = ''; } } } ); my $twig = new XML::Twig ( %constructor ) ; my $file = shift or die "usage: $0 perxml.xml"; $twig->parsefile($file);
and here is a sample xml file
<perl binary="/usr/bin/perl"> <use>strict warnings diagnostics</use> <scope> <my>$x $y $z</my> <set>$x 111</set> <set>$y 554</set> <set>$z <add>$x $y</add> </set> <print>$z</print> </scope> </perl>
</code>

Replies are listed 'Best First'.
Re: XML::Twig recursive constructor calls and local()
by mirod (Canon) on Aug 16, 2001 at 21:58 UTC

    princepawn, I never thought I would say this but it looks to me like you are not radical enough!

    Your language is not properly designed, $z=$x+$y should translate into

    <set> <var>z</var> <val> <add> <op1><var>x</var></op1> <op2><var>x</var></op2> </add> </val> </set>

    Then parsing becomes trivial.

    Anything less detailed does not make much sense, as it is still too close to Perl to be completely language independent (what's with the $x? Not to mention that <my>$x $y $z</my> for example does not properly tag each token leading to the repeated use of split in your handlers)

    Incidently it looks very much like what you are trying to define would be an XML version of the Perl internal op-tree. Maybe you should try to create an XML version of Perl 6 bytecode. That would probably make more sense than trying to create an ultra-verbose language that might not even map properly into Perl (how do you encode regexps, especially regexps including eval-el code?

    A reply falls below the community's threshold of quality. You may see it by logging in.