1

A short question: I want to give users of my script the possibility to enter arbitrary arrays, e.g. "[1 .. 5]" or "[1, 2, 6, 9 .. 20]". Currently, I just eval the given string (obviously not very secure!) and perlcritic gives me a Level 5 warning.

My current code:

my $arr = eval $patternData{"arr"};
unless ($arr) {
     # fixes <arr> values like "(1 .. 5)"
     $arr  = undef;
     @$arr = eval $patternData{"arr"};
}

EDIT: As many people are asking: This is a "the-more-the-better" thingy. I search a solution both powerful (in terms of expressiveness) and simple (in terms of debugging, parsing and writing input). Also: It absolutely has to support ranges (arrays with well above 100 entries should be entered easily)

and - as always - I forgot to include my version. 5.8 is a must, I'm working with vSphere API, which requires 5.8 (I miss ~~ so much !)

This is perl, v5.8.8 built for MSWin32-x86-multi-thread
(with 50 registered patches, see perl -V for more detail)

Copyright 1987-2006, Larry Wall

Binary build 820 [274739] provided by ActiveState http://www.ActiveState.com
Built Jan 23 2007 15:57:46
2
  • Would arbitrary arrays include things like [ map { ($_, $_+7 ) } split //, q(123)] or how much expressiveness do you really need? Commented Sep 25, 2012 at 8:00
  • check whether this helps. Commented Sep 25, 2012 at 8:03

2 Answers 2

4

How tied are you to your current input format?

One soulution could be to "change" the format to JSON and the use JSON::XS to deserialize your input. [1, 2, 3, 4] would still be valid input, but this would not allow [1, 2, 3, 4, 8 .. 10 ]

In all cases you will of course need to validate that the input data has the correct structure.

Sign up to request clarification or add additional context in comments.

2 Comments

Another alternative would be to use Parse::Range. This would give you ranges, but will only support a list of integers. (Which might be a plus).
Parse::Range looks pretty good, I'm not sure if it is sufficient (the code is part of a bigger project and the requirements evolve with the project ;) ) but it works as a medium-term solution at least! EDIT: Parse::Range isn't included in ppm :/
2

If your array only allows numbers, I'd opt for this pattern (being easy to parse):

[1, 2, 3, 4]

I further assume that there won't be nested arrays, e.g. [1, [2, 3]] or trailing commas [1,].

Then we just strip the array delimiters

my $line = <STDIN>;
$line =~ s/ ^\s*\[ | \]\s*$ //gx;

and split the array into a Perl array. We can either do this via split

my @array = split /,\s*/, $line;

or via extracting all numbers

my @array = ($line =~ /(\d+)(?:,\s*|$)/g);

The split solution would also allow including strings, e.g [1, two, 3], whereas the second solution is stricter (and therefore perhaps safer).

In a second step, you could parse ranges, e.g. via

@array = map {
   ($_ =~ /^(-?[\d.]+)\s*\.\.\s*(-?[\d.]+)/)
   ? ($1 .. $2)
   : $_
} @array;

If your data structures get more complex (recursing, quoting), I'd highly encourage writing a parser with Regexp::Grammars. This allows you to easily parse recursive structures, and almost anything else.

1 Comment

I thought about "regexing" too, but that will be kinda complex to debug if the format gets more complex. I'll definitely take a look at this grammar module!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.