in reply to VarStructor 1.0
Ok. Here is what I have so far; this is the result of about six hours work on your utility, trying to keep track of why I changed things the way I did.
I still don't think that this particular library is all that useful, and I agree with many of the other monks that it violates good coding guidelines left and right. It works for you, and I have no problems with that; the question is, would it work for other people?
The guidelines we put in place are not there to be memorized and followed by rote; there are more than a few debates about the finer points. But the overall goal is to make programming easier, less haphazard, more fun, and more reliable. Many languages and years of experience have shown that encapsulation, decoupling, data hiding, orthogonality, commenting, code-reuse, and not trying to outwit the language are all likely to make your life as a programmer more rewarding and productive. And I know that you do not consider yourself a professional or career programmer -- isn't it all the more important that you don't waste time and effort going down the same dead ends and roundabouts that others documented years ago?
Here's my list of points you might want to think about when you write code:
(And this is done on purpose; on at least some platforms, the shorthand \n is set to local EOL regardless. It is not always \012.)my $eol = qr/\015|\012|\015\012/;
$hash{ $something + $long / $and * $hairy } = $foo;
I would personally prefer:if ( /blah (foo) gibber/ ) { $var =~ s/\Q$1\E/bar/g; }
if ( /blah (foo) gibber/ ) { my $thingy = $1; $var =~ s/\Q$thingy\E/bar/g; }
Consider what happens when you have code that looks like this:$file =~ s/<<(\w+);.*\n\1\n//sg;
Since you don't limit the greediness, you'll miss that assignment to $varprint <<EOT; this is short. EOT $var = 'quux'; print <<EOT; this is also short; EOT
against this code:$file =~ s/[;\n]\s*#[^\n]*//sg; # Delete comments
Splat!print "foo; #bar";
Further, you can have multiple here-docs on one line:print <<HTML, $end_of_stream; ... ... HTML
print <<P1, <<P2; ... ... P1 ... ... P2
Your regex for matching subroutines does not take these into account.sub twiddle ( $ ) { my ( $arg ) = @_; ... }
Also note different indent for loop label, and the potential for having brackets on the same line. Forcing people to code in a particular style is evil. (And while we have our guidelines, they are all "we've found this to be a good idea, why don't you try it?" rather than "if you don't do it this way, my utility won't work.")LOOP: for ( my $I = do { ... }; $i < 20; ++$i ) { ... }
And here is my rework of the code so far. (This is version 15 in my local version control; I've been trying to keep different changes isolated. I can try to publish the entire sequence one way or another, if you're curious what sort of changes I've been making as "chunks").
#!/usr/bin/perl require 5.006; use strict; use warnings; # Copyright (c) 2004 Barry Kaminsky. All rights reserved. This program # is free software; you can redistribute it and/or modify it under the # same terms as Perl itself. # Last minute changes made May 2, 2004, 4:00 AM EST without much # testing # VarStructor is an alternative to Perl's reset function, which is # expected to be deprecated. It could also be used to print variables # and their values, including "my" variables. See comments at top of # sub VARSTRUCTOR for configuration information. ################################# # Test variables (could be deleted) %hash1=("key1"=>"value1","key2"=>"value2"); $hash2{"key1"}="value1"; $hash2{"key2"}="value2"; $Simple_Var = 'simple'; @Simple_Arr = ('simple1','simple2'); ################################# &VARSTRUCTOR('show', 'E: subs(VARSTRUCTOR)'); # Test parameters # my @unique = unique_elements @array; # # Return a sorted list of unique values out of all values passed in as # parameters. sub unique_elements { my %seen; return sort grep { ! $seen{$_}++ } @_; } sub VARSTRUCTOR { ########################################################## # 1st parameter: # Assign "show" to print variables and values or # "clear" to clear variables. For security reasons, the # default is clear. # set up default action my $action = 'clear'; # see if first arg is "show" or "clear" if ( $_[0] =~ /^\s*(show|clear)\s*$/i ) { shift; # ok, get rid of it $action = lc $1; } # 2nd parameter: # Comma-separated list of variables, subroutines, and # labels, whose variables will be included or excluded. # Labels must be of labeled blocks that are wrapped in # braces. This parameter must begin with "I:" or "E:" # (include or exclude). # # You can't include or exclude array elements, hash # keys or hash values. Legal variables for this # parameter begin with $, @, and %, followed by a # string of word characters not beginning with a digit. # # The label and subroutine name lists must be enclosed # in separate sets of parentheses, following the word # "labels" or "subs". A comma after the closing # parenthesis is necessary when another item in this # parameter follows. Commas also must separate the # labels and subroutine names within the parentheses. # Within the code to be parsed, there must be nothing # preceding the labels and the "sub"s on the # same line except for optional spaces, and the # subroutines and labeled blocks must end with the # next "}" that is at the same level of indentation as # the first character of the label or the "s" of "sub". # Within the parameters, the "&" is optional before # subroutine names and the ":" is optional after labels. my $include = 0; my $vars = ''; if ( $_[0] =~ /^\s([iIeE]):\s*(.*)$/) { shift; # get rid of it $include = 1 if lc $1 eq 'i'; $vars = $2; } # 3rd parameter: # Target file. Default is $0, indicating the file # VarStructor is being run from. # last arg is file name to read the source code from. defaults to # using the currently-executing script ($0). my $in_file_name = shift || $0; ########################################################## my $file = do { open my $fh, $in_file_name or die "opening '$in_file_name': $!"; local $/ = undef; # enter slurp mode <$fh>; }; # delete whole-line comments $file =~ s/^\s*#.*$//mg; ### Prevent parsing of some quoted strings by deleting here ### docs. Rarely, a single quoted string would be mistaken for a ### variable if not in a here doc. # Delete here docs with quoted identifiers $file =~ s/<<\s*('|"|`) ([^\n]*?([^\\]|[^\\]\\\\))\1 # Match here doc identifier, which # ends with an unescaped closing # quote. Limitation: an even number # of slashes greater than two at # the end of the identifier would # be wrongly interpreted as # escaping the quote and the here # doc value would probably not get # deleted. .*?\n\2\n//sgx; # Delete here docs with unquoted identifiers # (tkil) greediness will kill you here $file =~ s/<<(\w+);.*?\n\1\n//sg; # accumulators my $subs = ''; my $labels = ''; my $out_vars = ''; my @all_vars; my @hash_disp; # Isolate subroutines to search, according to $vars # look for subexpressions like: "subs ( my_sub_1, my_sub_2 )" # remove it from $vars, then add each subroutine to output. while ($vars =~ s/ subs \s* \( ( [^\)]+ ) \) \s* //x ) { my $subs = $1; foreach my $sub ( map quotemeta, split /,\s*/, $subs ) { # find the subroutine in the file, save the definition. $file =~ m! ( # entire expression in $1 ^ (\s*) # save indent for later ma +tching sub \s+ $sub \s*# actual decl start (?: \( [^\)]+ \) ) # skip prototypes \s* \{ # opening brace .*? # whatever (but note non-g +reedy) ^ \2 \} ) $ # look for brace at same i +ndent !xsm or die "couldn't find definition of sub '$su +b'"; # save the definition we just found $subs .= $1; } } # Isolate labeled blocks to search, according to $vars this time, # look for: labels( ... ) and remove every match from$vars as we # find them: while ($vars =~ s/ labels \s* \( ( [^\)]+ ) \) \s* //x ) { my $labels = $1; foreach my $label ( map quotemeta, split /,\s*/, $labels ) { $file =~ m! ( # whole expression ($1) ^ (\s*) # capture label indent ($2 +) $label: \s*? (^ \s*)? # capture loop indent, may +be ($3) (?: do | for(?:each)? \(.*?\) | while \(.*?\) | until \(.*?\) | ) \s*? \{ .*? # actual block contents ^ ( \2 | \3 ) \} # closing brace at either lab +el # or loop indent level !smx or die "couldn't find label '$label'" . " in '$in_file_name'"; # save it for later $labels .= $1; } # Delete or include individual variables, according to $vars while ($vars =~ s/([\$\@\%][^\d\W]\w*)//x) { my $this_var = $1; if ( $include ) { $out_vars .= "$this_var='';"; } else { $file =~ s/\Q$this_var\E//g; } } if ( $include ) { $file = "$out_vars $subs $labels"; } else { # (tkil) this looks completely bogus. WTH? $file =~ s/\Q($subs|$labels)\E//; } # Find arrays. If not an array used in push, require an equals sig +n # to avoid quoted email addresses that look like arrays. while (($file =~ s/([^\\]|[^\\]\\\\)(\@[^\d\W]\w*)\s*=//)|| # Find scalars/array elements after ++ or -- ($file =~ s/(?:[^\\]|[^\\]\\\\)(\+\+|--)\s*(\$[^\d\W]\w*(\[ +.*?\])?)//)|| # Find scalars/array elements before assignment operators, +"++", # "--", "." or ".=" ($file =~ s/([^\\]|[^\\]\\\\)(\$[^\d\W]\w*(\[.*?\])?)\s*(=| +\+=|-=|\*=|\/=|\%=|\*\*=|\+\+|--|\.)//)|| # Find arrays assigned to with push. ($file =~ s/push[^\w_]*([^\\]|[^\\]\\\\)(\@[^\d\W]\w*)//)) { my $this_var = $2; $this_var =~ s/^\$(.*)\[.*/\@$1/; # Convert element to its arr +ay my $eval_var; if ( $action eq 'clear' ) { $eval_var = $this_var =~ /^\$/ ? "$this_var='';" : "$this_ +var=();"; } else { # To do: print index numbers next to values $eval_var = "\\$this_var = $this_var\\n"; } push (@all_vars, $eval_var); } # Extract hashes while (($file =~ s/([^\\]|[^\\]\\\\)\%([^\d\W]\w*)\s*=//)|| ($file =~ s/([^\\]|[^\\]\\\\)\$([^\d\W]\w*)\{[^\n]*\}\s*=// +)) { my $this_hash = $2; if ( $action eq 'clear' ) { push @hash_disp, "\%$this_hash=();" } else { push @hash_disp, "print \"\\n\%$this_hash\\n\";" . "foreach \$key (sort(keys \%$this_hash))" . "{print \$key, '=', \$$this_hash\{\$key\}, \"\\n\";}" } } my $all_vars = join '', unique_elements @all_vars; if ( action eq 'show' ) { $all_vars =~ s/.*/print"$&";/; } eval $all_vars; my $hash_display = join '', unique_elements @hash_disp; eval $hash_display; }
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Re: VarStructor 1.0
by demerphq (Chancellor) on May 04, 2004 at 15:28 UTC | |
by tkil (Monk) on May 04, 2004 at 15:55 UTC | |
|