File Coverage

blib/lib/Math/Logic/Ternary/Calculator/Command.pm
Criterion Covered Total %
statement 74 140 52.8
branch 7 36 19.4
condition 2 12 16.6
subroutine 21 39 53.8
pod 0 13 0.0
total 104 240 43.3


line stmt bran cond sub pod time code
1             # Copyright (c) 2012-2017 Martin Becker, Blaubeuren. All rights reserved.
2             # This package is free software; you can redistribute it and/or modify it
3             # under the same terms as Perl itself.
4              
5             package Math::Logic::Ternary::Calculator::Command;
6              
7 4     4   51909 use 5.008;
  4         21  
8 4     4   18 use strict;
  4         9  
  4         69  
9 4     4   23 use warnings;
  4         7  
  4         105  
10 4     4   21 use Carp qw(croak);
  4         7  
  4         171  
11 4     4   1008 use Math::Logic::Ternary::Calculator::Version;
  4         9  
  4         94  
12 4     4   1136 use Math::Logic::Ternary::Calculator::Operator;
  4         13  
  4         200  
13              
14 4     4   30 use constant OP => Math::Logic::Ternary::Calculator::Operator::;
  4         9  
  4         202  
15              
16 4     4   22 use constant TC_MIN_ARGS => 0;
  4         7  
  4         146  
17 4     4   19 use constant TC_VAR_ARGS => 1;
  4         6  
  4         131  
18 4     4   19 use constant TC_CODE => 2;
  4         7  
  4         142  
19 4     4   20 use constant TC_DESCRIPTION => 3;
  4         9  
  4         5378  
20              
21             our $VERSION = '0.004';
22              
23             my %tool_commands = (); # name => [min_args, var_args, code, descr]
24             my @initial_commands = (); # index => [rank, code]
25             my $license = _read_own_license();
26              
27             _define_builtins();
28              
29             # ----- private subroutines -----
30              
31             sub _version {
32 2     2   8 print Math::Logic::Ternary::Calculator::Version->long_name, "\n";
33 2         4 return 1;
34             }
35              
36             sub _greeting {
37 2     2   5 _version();
38 2         4 print
39             qq{type "?" to get help, "/license" to display },
40             qq{license and copyright notice\n};
41 2         8 return 1;
42             }
43              
44 0     0   0 sub _quit { 0 }
45              
46             sub _license {
47 0     0   0 print $license;
48 0         0 return 1;
49             }
50              
51             sub _help {
52 0     0   0 my ($session, $topic) = @_;
53 0 0       0 if (!defined $topic) {
54 0         0 print
55             qq{Type "? " or "? " },
56             qq{to get help about a command or operator.\n},
57             qq{Available commands:\n},
58             join(q[ ], sort keys %tool_commands), "\n";
59 0         0 return 1;
60             }
61 0 0       0 if (exists $tool_commands{$topic}) {
62 0         0 my $descr = $tool_commands{$topic}->[TC_DESCRIPTION];
63 0 0       0 if (!defined $descr) {
64 0         0 $descr = "$topic (description not available)\n";
65             }
66 0         0 print $descr;
67 0         0 return 1;
68             }
69 0 0       0 if ('/' eq substr $topic, 0, 1) {
70 0         0 print "$topic: unknown command\n";
71 0         0 return 1;
72             }
73 0         0 my $mode = $session->state->mode;
74 0         0 my $op = OP->find($topic, $mode); # TODO: abstraction
75 0 0       0 if (!ref $op) {
76 0         0 print "$topic: $op\n";
77 0         0 return 1;
78             }
79 0         0 print $op->description($mode);
80 0         0 return 1;
81             }
82              
83             sub _read_own_license {
84 4     4   20 local $/ = q[];
85 4         8 my $text = q[];
86 4         7 my $copy = 0;
87 4         25 while (defined(my $par = )) {
88 92 100       280 if ($copy) {
    100          
89 12         19 $par =~ s/^=head1 (?=DISCLAIMER)//;
90 12         36 $par =~ s/\blibrary\b/application/g;
91 12 100       35 last if $par =~ /^=/;
92 8         46 $text .= $par;
93             }
94             elsif ($par =~ s/^=head1 (?=COPYRIGHT)//) {
95 4         8 $copy = 1;
96 4         15 $text = $par;
97             }
98             }
99 4         29 close DATA;
100 4 50       13 die "assertion failed: missing copyright notice" if !$copy;
101 4         17 return $text;
102             }
103              
104             sub _define_builtins {
105 4     4   8 my $class = caller;
106 4         27 $class->def_initial_command(0, \&_greeting);
107 4         13 $class->def_tool_command('/version', 0, 0, \&_version , <<'EOT');
108             /version
109             display software version
110             EOT
111 4         13 $class->def_tool_command('/quit', 0, 0, \&_quit , <<'EOT');
112             /quit
113             quit calculator session
114             EOT
115 4         12 $class->def_tool_command('/license', 0, 0, \&_license , <<'EOT');
116             /license
117             display license and copyright notice
118             EOT
119 4         11 $class->def_tool_command('?', 0, 1, \&_help , <<'EOT');
120             ? [command|operator]
121             show general help text or describe a command or operator
122             EOT
123             }
124              
125             # ----- class methods -----
126              
127             sub def_initial_command {
128 8     8 0 20 my ($class, $rank, $code, @args) = @_;
129 8         16 my $pos = @initial_commands;
130 8   66     42 while ($pos && $initial_commands[$pos-1]->[0] > $rank) {
131 0         0 --$pos;
132             }
133 8         20 my $cmd = $class->custom_command($code, @args);
134 8         25 splice @initial_commands, $pos, 0, [$rank, $cmd];
135 8         16 return;
136             }
137              
138             sub get_initial_commands {
139 2     2 0 5 my ($class) = @_;
140 2         4 return map { $_->[1] } @initial_commands;
  6         17  
141             }
142              
143             sub def_tool_command {
144 37     37 0 76 my ($class, $name, $min_args, $var_args, $code, $descr) = @_;
145 37         89 $tool_commands{$name} = [$min_args, $var_args, $code, $descr];
146 37         70 return;
147             }
148              
149             sub greeting_command {
150 0     0 0 0 my ($class) = @_;
151 0         0 my $cmd = \&_greeting;
152 0         0 return bless $cmd, $class;
153             }
154              
155             sub unknown_command {
156 0     0 0 0 my ($class, $name) = @_;
157             return bless sub {
158 0     0   0 print "unknown command: $name\n";
159 0         0 return 1;
160 0         0 }, $class;
161             }
162              
163             sub unknown_operator {
164 0     0 0 0 my ($class, $name, $comment) = @_;
165             return bless sub {
166 0     0   0 print "unknown operator: $name: $comment\n";
167 0         0 return 1;
168 0         0 }, $class;
169             }
170              
171             sub not_implemented {
172 0     0 0 0 my ($class, $name) = @_;
173             return bless sub {
174 0     0   0 print "internal error: method not implemented: $name\n";
175 0         0 return 1;
176 0         0 }, $class;
177             }
178              
179             sub wrong_usage {
180 0     0 0 0 my ($class, $reason) = @_;
181             return bless sub {
182 0     0   0 print "wrong usage: $reason\n";
183 0         0 return 1;
184 0         0 }, $class;
185             }
186              
187             sub bad_value {
188 0     0 0 0 my ($class, $reason) = @_;
189             return bless sub {
190 0     0   0 print "bad value: $reason\n";
191 0         0 return 1;
192 0         0 }, $class;
193             }
194              
195             sub _WRONG_ARGC {
196 0     0   0 my ($min_args, $var_args, $act_args) = @_;
197             return
198 0   0     0 $act_args < $min_args ||
199             0 <= $var_args && $min_args + $var_args < $act_args;
200             }
201              
202             sub check_argc {
203 0     0 0 0 my ($class, $name, $min_args, $var_args, $act_args) = @_;
204 0         0 my $max_args = $min_args + $var_args;
205 0 0 0     0 if ($min_args <= $act_args && ($var_args < 0 || $act_args <= $max_args)) {
      0        
206 0         0 return q[];
207             }
208 0 0       0 my $n =
    0          
    0          
    0          
209             1 < $var_args? "$min_args .. $max_args":
210             1 == $var_args? "$min_args or $max_args":
211             0 > $var_args? "at least $min_args":
212             $min_args? "exactly $min_args" : 'no';
213 0 0       0 my $s = 1 == $max_args? '': 's';
214 0         0 return qq{$name takes $n argument$s};
215             }
216              
217             sub tool_command {
218 0     0 0 0 my ($class, $name, @args) = @_;
219 0 0       0 if ($name =~ s{^\?([/?\w].*)}{?}s) {
220 0         0 unshift @args, $1; # treat "?foo" as ("?", "foo")
221             }
222 0 0       0 return $class->unknown_command($name) if !exists $tool_commands{$name};
223 0         0 my ($min_args, $var_args, $code) = @{$tool_commands{$name}};
  0         0  
224 0 0       0 if (my $error = $class->check_argc($name, $min_args, $var_args, 0+@args)) {
225 0         0 return $class->wrong_usage($error);
226             }
227 0     0   0 return bless sub { $code->($_[0], @args) }, $class;
  0         0  
228             }
229              
230             sub custom_command {
231 10     10 0 27 my ($class, $code, @args) = @_;
232 10     8   43 return bless sub { $code->($_[0], @args) }, $class;
  8         16  
233             }
234              
235             # ----- object methods -----
236              
237 8     8 0 10 sub execute { my $cmd = shift; $cmd->(@_) }
  8         16  
238              
239             1;
240             __DATA__