File Coverage

blib/lib/Getopt/EX/RPN.pm
Criterion Covered Total %
statement 41 43 95.3
branch 7 10 70.0
condition 3 8 37.5
subroutine 11 11 100.0
pod 0 2 0.0
total 62 74 83.7


line stmt bran cond sub pod time code
1             package Getopt::EX::RPN;
2              
3             our $VERSION = "0.01";
4              
5             =encoding utf-8
6              
7             =head1 NAME
8              
9             Getopt::EX::RPN - RPN calculation module for Getopt::EX command option
10              
11             =head1 SYNOPSIS
12              
13             use Getopt::EX::RPN qw(rpn_calc);
14              
15             =head1 DESCRIPTION
16              
17             Getopt::EX::RPN is a wrapper for L package which implement
18             Reverse Polish Notation calculation. B function in this
19             package takes additional C and C token which describe
20             terminal height and width.
21              
22             B recognize following tokens (case-insensitive) and numbers,
23             and ignore anything else. So you can use any other character as a
24             delimiter. Delimiter is not necessary if token boundary is clear.
25              
26             HEIGHT WIDTH
27             { }
28             +,ADD ++,INCR -,SUB --,DECR *,MUL /,DIV %,MOD POW SQRT
29             SIN COS TAN
30             LOG EXP
31             ABS INT
32             &,AND |,OR !,NOT XOR ~
33             <,LT <=,LE =,==,EQ >,GT >=,GE !=,NE
34             IF
35             DUP EXCH POP
36             MIN MAX
37             TIME
38             RAND LRAND
39              
40             Since module L uses comma to separate parameters,
41             you can't use comma as a token separator in RPN expression. This
42             package accept expression like this:
43              
44             &set(width=WIDTH:2/,height=HEIGHT:DUP:2%-2/)
45              
46             =head1 AUTHOR
47              
48             Kazumasa Utashiro
49              
50             =head1 LICENSE
51              
52             Copyright Kazumasa Utashiro.
53              
54             This library is free software; you can redistribute it and/or modify
55             it under the same terms as Perl itself.
56              
57             =cut
58              
59 2     2   63853 use v5.14;
  2         15  
60 2     2   9 use warnings;
  2         3  
  2         72  
61 2     2   9 use Carp;
  2         4  
  2         98  
62              
63 2     2   10 use Exporter 'import';
  2         2  
  2         200  
64             our @EXPORT_OK = qw( rpn_calc );
65              
66             my @operator = sort { length $b <=> length $a } split /[,\s]+/, <<'END';
67             HEIGHT WIDTH
68             { }
69             +,ADD ++,INCR -,SUB --,DECR *,MUL /,DIV %,MOD POW SQRT
70             SIN COS TAN
71             LOG EXP
72             ABS INT
73             &,AND |,OR !,NOT XOR ~
74             <,LT <=,LE =,==,EQ >,GT >=,GE !=,NE
75             IF
76             DUP EXCH POP
77             MIN MAX
78             TIME
79             RAND LRAND
80             END
81              
82 2     2   526 use Data::Dumper;
  2         5842  
  2         190  
83              
84             my $operator_re = join '|', map quotemeta, @operator;
85             my $term_re = qr/(?:\d*\.)?\d+|$operator_re/i;
86             my $rpn_re = qr/(?: $term_re ,* ){2,}/xi;
87              
88             tie my %terminal, __PACKAGE__;
89              
90             sub rpn_calc {
91 2     2   868 use Math::RPN ();
  2         4437  
  2         446  
92 11     11 0 3909 my @terms = map { /$term_re/g } @_;
  21         217  
93 11         24 for (@terms) {
94 78 100       141 if (/^(?:HEIGHT|WIDTH)$/i) {
95 5         26 $_ = $terminal{$_}
96             }
97             }
98 11         12 my @ans = do { local $_; Math::RPN::rpn @terms };
  11         11  
  11         26  
99 11 50 33     1532 if (@ans == 1 && defined $ans[0] && $ans[0] !~ /[^\.\d]/) {
      33        
100 11         90 $ans[0];
101             } else {
102 0         0 undef;
103             }
104             }
105              
106             sub TIEHASH {
107 3     3   73 my $pkg = shift;
108 3         15 bless { HEIGHT => undef, WIDTH => undef }, $pkg;
109             }
110              
111             sub FETCH {
112 7     7   33 my $obj = shift;
113 7         13 my $key = uc shift;
114 7 100       19 if (not defined $obj->{$key}) {
115 2         4 ($obj->{HEIGHT}, $obj->{WIDTH}) = terminal_size();
116             }
117 7   50     38 $obj->{$key} // die;
118             }
119              
120             sub terminal_size {
121 2     2   862 use Term::ReadKey;
  2         3318  
  2         244  
122 2     2 0 5 my @default = (80, 24);
123 2         2 my @size;
124 2 50       51 if (open my $tty, ">", "/dev/tty") {
125             # Term::ReadKey 2.31 on macOS 10.15 has a bug in argument handling
126             # and the latest version 2.38 fails to install.
127             # This code should work on both versions.
128 0         0 @size = GetTerminalSize $tty, $tty;
129             }
130 2 50       19 @size ? @size : @default;
131             }
132              
133             1;
134              
135             # LocalWords: RPN rpn calc