File Coverage

blib/lib/Math/Logic/Ternary/Calculator/Session.pm
Criterion Covered Total %
statement 66 209 31.5
branch 3 46 6.5
condition 0 9 0.0
subroutine 20 38 52.6
pod 0 20 0.0
total 89 322 27.6


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::Session;
6              
7 2     2   48511 use 5.008;
  2         6  
8 2     2   10 use strict;
  2         3  
  2         38  
9 2     2   10 use warnings;
  2         5  
  2         67  
10 2     2   20 use Carp qw(croak);
  2         3  
  2         84  
11 2     2   523 use Math::Logic::Ternary::Calculator::Parser;
  2         7  
  2         52  
12 2     2   12 use Math::Logic::Ternary::Calculator::Command;
  2         5  
  2         31  
13 2     2   412 use Math::Logic::Ternary::Calculator::State;
  2         5  
  2         40  
14 2     2   10 use Math::Logic::Ternary::Calculator::Mode;
  2         4  
  2         31  
15 2     2   7 use Math::Logic::Ternary::Calculator::Operator;
  2         5  
  2         57  
16              
17             our $VERSION = '0.004';
18              
19 2     2   8 use constant _STATE => 0;
  2         4  
  2         86  
20 2     2   9 use constant PARSER => Math::Logic::Ternary::Calculator::Parser::;
  2         4  
  2         69  
21 2     2   9 use constant CMD => Math::Logic::Ternary::Calculator::Command::;
  2         5  
  2         71  
22 2     2   10 use constant MODE => Math::Logic::Ternary::Calculator::Mode::;
  2         4  
  2         69  
23 2     2   10 use constant OPERATOR => Math::Logic::Ternary::Calculator::Operator::;
  2         3  
  2         69  
24              
25 2     2   10 use constant MAX_COLUMNS => 70;
  2         2  
  2         3616  
26              
27             sub new {
28 2     2 0 5 my ($class, $state) = @_;
29 2         5 return bless [$state], $class;
30             }
31              
32 4     4 0 8 sub state { $_[0]->[_STATE] }
33 0     0 0 0 sub word_size { $_[0]->[_STATE]->word_size }
34              
35             sub fetch_value {
36 0     0 0 0 my ($this, $index) = @_;
37 0         0 my $state = $this->state;
38 0         0 my $value = $state->fetch($index);
39 0 0       0 return $value if defined $value;
40 0         0 my $min = $state->min_index;
41 0         0 my $max = $state->max_index;
42 0 0       0 croak "no numbered values stored so far" if $max < $min;
43 0         0 croak "index $index out of range $min..$max";
44             }
45              
46             sub recall_value {
47 0     0 0 0 my ($this, $name) = @_;
48 0         0 my $state = $this->state;
49 0         0 my $value = $state->recall($name);
50 0 0       0 return $value if defined $value;
51 0         0 croak "variable $name not yet defined";
52             }
53              
54             sub _display_this {
55 0     0   0 my ($state, $name, $value) = @_;
56 0         0 my $str = $state->format_value($name, $value);
57 0         0 print $str, "\n";
58 0         0 return 1;
59             }
60              
61             sub storage_append {
62 0     0 0 0 my ($this, @values) = @_;
63 0         0 my $state = $this->state;
64 0         0 foreach my $value (@values) {
65 0         0 my ($new_i) = $state->append($value);
66 0         0 _display_this($state, "#$new_i", $value);
67             }
68 0         0 return 1;
69             }
70              
71             sub storage_store {
72 0     0 0 0 my ($this, $name, $value) = @_;
73 0         0 my $state = $this->state;
74 0         0 $state->store($name, $value);
75 0         0 return _display_this($state, $name, $value);
76             }
77              
78             CMD->def_tool_command('?#', 0, 0, \&list_numbered_cmd, <<'EOT');
79             ?#
80             list numbered values of this session
81             EOT
82             sub list_numbered_cmd {
83 0     0 0 0 my ($this) = @_;
84 0         0 my $state = $this->state;
85 0         0 my $max = $state->max_index;
86 0 0       0 if ($max < 0) {
87 0         0 print "no numbered values stored so far\n";
88             }
89             else {
90 0         0 foreach my $index (0 .. $max) {
91 0         0 my $value = $state->fetch($index);
92 0         0 _display_this($state, "#$index", $value);
93             }
94             }
95 0         0 return 1;
96             }
97              
98             CMD->def_tool_command('?=', 0, 0, \&list_named_cmd, <<'EOT');
99             ?=
100             list variables of this session
101             EOT
102             sub list_named_cmd {
103 0     0 0 0 my ($this) = @_;
104 0         0 my $state = $this->state;
105 0         0 my @names = $state->all_names;
106 0 0       0 if (!@names) {
107 0         0 print "no variables defined so far\n";
108             }
109             else {
110 0         0 foreach my $name (@names) {
111 0         0 my $value = $state->recall($name);
112 0         0 _display_this($state, $name, $value);
113             }
114             }
115 0         0 return 1;
116             }
117              
118             CMD->def_initial_command(10, \&size_cmd);
119             CMD->def_tool_command('/size', 0, 0, \&size_cmd, <<'EOT');
120             /size
121             display word size of this session
122             EOT
123             sub size_cmd {
124 2     2 0 5 my ($this) = @_;
125 2         5 my $state = $this->state;
126 2         8 my $size = $state->word_size;
127 2         7 print "word size is $size trits\n";
128 2         7 return 1;
129             }
130              
131             CMD->def_initial_command(20, \&mode_cmd, undef);
132             CMD->def_tool_command('/mode', 0, 1, \&mode_cmd, <<'EOT');
133             /mode [new_mode]
134             show current arithmetic mode or set new arithmetic mode
135             EOT
136             sub mode_cmd {
137 2     2 0 5 my ($this, @modes) = @_;
138 2         5 my $state = $this->state;
139 2         4 my $help = !@modes;
140 2         4 foreach my $new_mode (@modes) {
141 2 50       6 next if !defined $new_mode;
142 0         0 my $mode = MODE->from_string($new_mode);
143 0 0       0 if (defined $mode) {
144 0         0 $state->set_mode($mode);
145             }
146             else {
147 0         0 ++$help;
148             }
149             }
150 2         5 print "arithmetic mode is ", $state->mode->name, "\n";
151 2 50       5 if ($help) {
152 0         0 my @all_modes = MODE->modes;
153 0         0 my $i = 0;
154             print
155             q{valid modes are },
156 0         0 join(q[, ], map { $i++ . q[ = ] . $_->name } @all_modes), "\n";
  0         0  
157             }
158 2         7 return 1;
159             }
160              
161             CMD->def_tool_command('/range', 0, 0, \&range_cmd, <<'EOT');
162             /range
163             return two words: smallest and largest possible integer
164             (dependent on word size and current arithmetic mode)
165             EOT
166             sub range_cmd {
167 0     0 0 0 my ($this) = @_;
168 0         0 my $state = $this->state;
169 0         0 foreach my $value ($state->range) {
170 0         0 my ($new_i) = $state->append($value);
171 0         0 _display_this($state, "#$new_i", $value);
172             }
173 0         0 return 1;
174             }
175              
176             CMD->def_tool_command('/rand', 0, 0, \&rand_cmd, <<'EOT');
177             /rand
178             return a word with random trits
179             EOT
180             sub rand_cmd {
181 0     0 0 0 my ($this) = @_;
182 0         0 my $state = $this->state;
183 0         0 my $value = $state->rand;
184 0         0 my ($new_i) = $state->append($value);
185 0         0 _display_this($state, "#$new_i", $value);
186 0         0 return 1;
187             }
188              
189             CMD->def_tool_command('/abc', 0, 1, \&abc_cmd, <<'EOT');
190             /abc [n]
191             return n (default 3) words covering all different trit combinations
192             (trits ordered according to current arithmetic mode)
193             EOT
194             sub abc_cmd {
195 0     0 0 0 my ($this, $dim) = @_;
196 0 0       0 $dim = 3 if !defined $dim;
197 0         0 my $state = $this->state;
198 0         0 my $max_abc = $state->max_abc;
199 0 0 0     0 if ($dim !~ /^\d+\z/ || 0 == $dim || $max_abc < $dim) {
      0        
200 0         0 print "usage: /abc [n] (where n is in 1 .. $max_abc)\n";
201 0         0 return 1;
202             }
203 0         0 my @abc = $state->abc($dim);
204 0         0 foreach my $value (@abc) {
205 0         0 my ($new_i) = $state->append($value);
206 0         0 _display_this($state, "#$new_i", $value);
207             }
208 0         0 return 1;
209             }
210              
211             CMD->def_tool_command('/reset', 0, 1, \&reset_cmd, <<'EOT');
212             /reset [1|2]
213             discard all stored values, or all numbered values (1) or all variables (2)
214             EOT
215             sub reset_cmd {
216 0     0 0 0 my ($this, $what) = @_;
217 0         0 my $state = $this->state;
218 0         0 my $count = join '+', eval { $state->reset($what) };
  0         0  
219 0 0       0 if (q[] eq $count) {
220 0         0 print "bad argument: 1 or 2 expected\n";
221             }
222             else {
223 0         0 print "discarded $count value(s)\n";
224             }
225 0         0 return 1;
226             }
227              
228             sub _headline {
229 0     0   0 my ($title) = @_;
230 0         0 my $len = 2 + length $title;
231 0 0       0 return "$title\n" if $len > MAX_COLUMNS;
232 0         0 my $lpad = q[-] x ((MAX_COLUMNS - $len) >> 1);
233 0         0 my $rpad = q[-] x ((MAX_COLUMNS + 1 - $len) >> 1);
234 0         0 return "$lpad $title $rpad\n";
235             }
236              
237             sub _with_line_breaks {
238 0     0   0 my @words = @_;
239 0         0 my $str = q[];
240 0         0 my $cols = 0;
241 0         0 foreach my $word (@words) {
242 0         0 my $len = length $word;
243 0 0 0     0 if ($cols && $cols + $len >= MAX_COLUMNS) {
244 0         0 $str .= "\n";
245 0         0 $cols = 0;
246             }
247 0 0       0 if ($cols) {
248 0         0 $str .= q[ ];
249 0         0 ++$cols;
250             }
251 0         0 $str .= $word;
252 0         0 $cols += $len;
253             }
254 0 0       0 if ($cols) {
255 0         0 $str .= "\n";
256             }
257 0         0 return $str;
258             }
259              
260             CMD->def_tool_command('/ops', 0, -1, \&list_operators_cmd, <<'EOT');
261             /ops [n]...
262             list all operators, or operators of kind n (n = 0, 1, 2, 3...)
263             EOT
264             sub list_operators_cmd {
265 0     0 0 0 my ($this, @kinds) = @_;
266 0         0 my $mode = $this->state->mode;
267 0         0 my @op_kinds = OPERATOR->operator_kinds;
268 0 0       0 if (grep { !/^\d+\z/ || $_ >= @op_kinds } @kinds) {
  0 0       0  
269 0         0 print "usage: /ops [n]... (where n is in 0..$#op_kinds)\n";
270 0         0 return 1;
271             }
272 0 0       0 if (!@kinds) {
273 0         0 @kinds = 0 .. $#op_kinds;
274             }
275 0         0 my $spacer = q[];
276 0         0 foreach my $kind (@kinds) {
277 0         0 print
278             $spacer,
279             _headline($op_kinds[$kind]),
280             _with_line_breaks(OPERATOR->operator_list($mode, $kind));
281 0         0 $spacer = "\n";
282             }
283 0         0 return 1;
284             }
285              
286             sub operand_from_integer {
287 0     0 0 0 my ($this, $int) = @_;
288 0         0 return $this->state->convert_int($int);
289             }
290              
291             sub operand_from_string {
292 0     0 0 0 my ($this, $string) = @_;
293 0         0 return $this->state->convert_string($string);
294             }
295              
296             sub execute_operator {
297 0     0 0 0 my ($this, $raw_name, @operands) = @_;
298 0         0 my $state = $this->state;
299 0         0 my $op = OPERATOR->find($raw_name, $state->mode);
300 0 0       0 if (!ref $op) {
301 0         0 return CMD->unknown_operator($raw_name, $op)->execute($this);
302             }
303 0         0 my ($minc, $varc, $retv) = $op->signature;
304 0 0       0 if (my $error = CMD->check_argc($raw_name, $minc, $varc, 0+@operands)) {
305 0         0 return CMD->wrong_usage($error)->execute($this);
306             }
307 0         0 my (@results) = $op->execute($state->normalize_operands(@operands));
308 0 0       0 if (!@results) {
309 0         0 print "operation produced no output\n";
310 0         0 return 1;
311             }
312 0 0       0 if ($retv) {
313 0         0 return $this->storage_append(@results);
314             }
315 0         0 foreach my $value (@results) {
316 0         0 print $value, "\n";
317             }
318 0         0 return 1;
319             }
320              
321             sub run {
322 2     2 0 4 my ($this, $input) = (@_, '-');
323 2         6 $| = 1; # turn on auto-flush mode for STDOUT
324 2         9 my $parser = PARSER->open($input);
325 2         5 while (my $command = $parser->read_command) {
326 8 50       15 last if !$command->execute($this);
327             }
328 2         5 $parser->close;
329             }
330              
331             1;
332             __END__