File Coverage

blib/lib/Repl/Spec/Args/VarArgList.pm
Criterion Covered Total %
statement 71 71 100.0
branch 10 14 71.4
condition 12 22 54.5
subroutine 5 5 100.0
pod 0 2 0.0
total 98 114 85.9


line stmt bran cond sub pod time code
1             package Repl::Spec::Args::VarArgList;
2            
3 1     1   3097 use strict;
  1         2  
  1         49  
4 1     1   85 use warnings;
  1         3  
  1         60  
5 1     1   6 use Carp;
  1         2  
  1         1547  
6            
7             # Parameters:
8             # - Array ref of fixed args.
9             # - A single var arg.
10             # - Min nr. occurences (can be -1 if not checked.)
11             # - Max nr. occurences (can be -1 if unlimited.)
12             # - Array ref of named args.
13             sub new
14             {
15 1     1 0 521 my $invocant = shift;
16 1   33     7 my $class = ref($invocant) || $invocant;
17            
18 1   50     48 my $fixedArg = shift || die "Expected a arrayref containing fixed args.";
19 1   50     9 my $varArg = shift || die "Expected a single var arg.";
20 1         2 my $min = shift;
21 1         1 my $max = shift;
22 1   50     4 my $namedArg = shift || die "Expected a arrayref containing named args.";
23            
24 1         2 my $self= {};
25 1         3 $self->{FIXED}=$fixedArg;
26 1         3 $self->{VAR}=$varArg;
27 1         3 $self->{MIN} = $min;
28 1         3 $self->{MAX} = $max;
29 1         2 $self->{NAMED}=$namedArg;
30            
31 1         6 return bless $self, $class;
32             }
33            
34             # Parameters:
35             # - An argument list (ref to array).
36             # - A context!
37             # Notes:
38             # - The context is not used directly by the argument checker, it is passed to the
39             # type specs, so a type spec implementation could make use of it.
40             # - The argument list is expected to have the form ["cmd", arg1, ..., argn]
41             # - The result list contains
42             # * First the fixt args.
43             # * !!! Secondly the named args. Otherwise it would be impossible to
44             # differentiate from the varargs.
45             # * Finally the var args.
46             sub guard
47             {
48 4     4 0 3170 my $self = shift;
49 4   50     13 my $args = shift || die "Argument list expected.";
50 4         7 my $argslen = scalar(@$args);
51 4   50     10 my $ctx = shift || die "Context expected";
52            
53 4         10 my $fixed = $self->{FIXED};
54 4         7 my $var = $self->{VAR};
55 4         8 my $min = $self->{MIN};
56 4         7 my $max = $self->{MAX};
57 4         7 my $named = $self->{NAMED};
58            
59             # We look for all pairs at the end of the argument list. We will only
60             # consider these trailing pairs.
61 4         5 my $startnamed = $argslen - 1;
62 4   66     30 while ($startnamed > 0 && (ref($args->[$startnamed]) eq "Repl::Core::Pair"))
63             {
64 3         34 $startnamed = $startnamed - 1;
65             }
66 4         9 my $nrvar = $startnamed - scalar(@$fixed);
67 4 50       10 $nrvar = 0 if $nrvar < 0;
68 4 100 66     29 croak sprintf("Too few arguments of type '%s'. Expected at least %d and received %d.", $var->specname(), $min, $nrvar) if($min >= 0 && $nrvar < $min);
69 3 100 66     24 croak sprintf("Too many arguments of type '%s'. Expected at most %d and received %d.", $var->specname(), $max, $nrvar) if($max >= 0 && $nrvar > $max);
70            
71 2         3 my $newargs = [];
72 2         5 $newargs->[0] = $args->[0];
73            
74             # Argidx will be used for the different parameter types.
75 2         4 my $argidx = 1;
76            
77             # Test the fixed args.
78 2 50       8 croak sprintf("Too few arguments. Expected at least %d arguments but received %d.", scalar(@$fixed), ($argslen - 1)) if ((scalar($args) - 1) < scalar(@$fixed));
79 2         2 my $i = 0;
80 2         6 while ($i < scalar(@$fixed))
81             {
82 2         15 $newargs->[$argidx] = $fixed->[$i]->guard($args, $argidx, $ctx);
83 2         4 $i = $i + 1;
84 2         5 $argidx = $argidx + 1;
85             }
86            
87             # Now we can resolve the named arguments within this range.
88 2         4 foreach my $spec (@$named)
89             {
90 2         14 $newargs->[$argidx] = $spec->guard($args, $startnamed, $ctx);
91 2         6 $argidx = $argidx + 1;
92             }
93             # Finally we go looking for spurious named parameters that were not specified ...
94 2         3 my $j = $startnamed;
95 2         7 while ($j < $argslen)
96             {
97 3 100       8 if(ref($args->[$j]) eq "Repl::Core::Pair")
98             {
99 1         3 my $pair = $args->[$j];
100 1         3 my $left = $pair->getLeft();
101 1         4 my $right = $pair->getRight();
102 1         1 my $found = 0;
103            
104 1         3 foreach my $namedpar (@$named)
105             {
106 1 50       5 if($namedpar->name() eq $left)
107             {
108 1         2 $found = 1;
109 1         3 next;
110             }
111             }
112 1 50       5 croak sprintf("Found an unexpected named argument '%s'.", $left) if ! $found;
113             }
114 3         13 $j = $j + 1;
115             }
116            
117             # Check the var ones.
118             # Start looking after the fixed args, provide the expected index.
119 2         4 my $m = 0;
120 2         4 my $startvar = scalar(@$fixed) + 1;
121 2         6 while($m < $nrvar)
122             {
123 6         24 $newargs->[$argidx] = $var->guard($args, $startvar + $m, $ctx);
124 6         7 $m = $m + 1;
125 6         13 $argidx = $argidx + 1;
126             }
127            
128             # If we get here, we've done all the checking.
129 2         7 return $newargs;
130             }
131            
132             1;