X-Git-Url: http://git.shiar.net/descalc.git/blobdiff_plain/b2aba05a22d1f4036008c2d12874ddc9eb98edc0..7d9af85556b0e64c70e3641764b7b49a306723f3:/dct.pl diff --git a/dct.pl b/dct.pl index a671e52..b11ca57 100755 --- a/dct.pl +++ b/dct.pl @@ -2,17 +2,17 @@ # DCT - desktop calculator thingy -# reverse polish notition calculator using curses +# simple modular reverse polish notition calculator # by Shiar -our $VERSION = 1.009; - use strict; use warnings; use utf8; +use Data::Dumper; use Term::ReadKey; -use Curses; + +our $VERSION = "1.10.6"; use vars qw(@stack %val %var %set %alias %action %hook); @@ -27,47 +27,39 @@ use vars qw(@stack %val %var %set %alias %action %hook); width => 42, # limit value precision, stetch menu ); # %set -%alias = (' '=>'enter', "\004"=>'quit', 'q'=>'quit'); # rudimentary default key bindings +%alias = (' '=>"enter", "\004"=>"quit"); # rudimentary default key bindings %action = ( - "chs" => [1, sub { -$_[0] }], # negative - - "drop" => [1, sub { defined $val{i} ? '' : () }], # drop - "back" => [1, sub { () }], # drop essentially - "clear" => [0, sub { @stack = (); undef %val; () }], # clear all #todo: if (val{i}) delete char after cursor - - "enter" => [0, sub { + "enter" => [ 0, sub { local $_ = defined $val{i} ? $val{i} : $stack[0]; undef %val; return defined $_ ? $_ : (); }], # duplication - "swap" => [2, sub { reverse @_ }], # swap x<->y - "undo" => [-1, sub { - ($var{undo}, @stack) = ([@stack], @{ $var{undo} }); - }], # undo/redo - "stack" => [-1, sub { + "chs" => [ 1, sub { -$_[0] }], # negative + + "drop" => [ 1, sub { defined $val{i} ? '' : () }], # drop + "back" => [ 1, sub { () }], # drop essentially + "clear" => [ 0, sub { @stack = (); undef %val; () }], # clear all + + "swap" => [ 2, sub { reverse @_ }], # swap x<->y + "stack" => [-2, sub { $var{stackpos} = 0 unless $var{stackpos}; # initialize $var{stackpos} %= @stack; # cycle $val{i} = $stack[$var{stackpos}++]; }], # stack - "version" => [-1, sub { error("Desktop Calculator Thingy $VERSION by Shiar"); () }], # version + "sto" => [ 1, sub { $var{a} = $_[0] }], # copy + '?' => [ 1, sub { $var{a} = $_[0] }], # assign - "sto" => [1, sub { $var{a} = $_[0] }], # copy - '?' => [1, sub { $var{a} = $_[0] }], # assign + "version" => [-2, sub { + error("Desktop Calculator Thingy $VERSION by Shiar"); () + }], # version ); # %action sub error($) { - attron(A_REVERSE); - addstr(0, 0, shift); - attroff(A_REVERSE); - clrtoeol; - refresh; - - ReadKey; # wait for confirm - 1 while defined ReadKey(-1); # clear key buffer + $_->($_[0]) for @{$hook{showerror}}; } # error sub showval($$); @@ -110,46 +102,41 @@ sub showval($$) { } # showval sub showstack() { - for (0..@stack-1) { - addstr($set{height}-$_, 1, "$_: ".showval($stack[$_], $set{base})); - clrtoeol; - } # show stack - clrtoeol($set{height}-@stack, 1); + $_->() for @{$hook{showstack}}; } # showstack -my @modules; -eval 'require $_' ? push @modules, $_ -: print STDERR "error loading $_\n".(join "", map "\t$_\n", split /\n/, $@) - for glob "*.pm"; +my %modules; +for my $module (sort glob "*.pm") { + next unless $module =~ /^\d{2}_(\w+)\.pm$/; # filename 00_name.pm + next if defined $modules{$1}; # such module already loaded + defined ($_ = do $module) + ? (ref $_ and $modules{$1} = $_) # return value means no errors + : print STDERR $@, "error loading $module\n\n"; +} # load modules + +printf STDERR "DCT %s by Shiar (%s)\n", $VERSION, + join "; ", map {"$_ $modules{$_}{version}"} keys %modules; -initscr; ReadMode 3; # cbreak mode -END { - ReadMode 0; - endwin; -} # restore terminal on quit - -$set{height} = $LINES-2 if $LINES>=3; -$set{width} = $COLS if $COLS; -$_->() for @{ $hook{init} }; - - -DRAW: -clear; -$_->() for @{ $hook{refresh} }; -showstack(); -addstr($set{height}+1, 0, "> "); # prompt - -LOOP: -while (1) { - addstr($set{height}+1, 2, showval($val{i}, $set{base})); - for my $cmd (@{ $hook{showentry} }) { - addstr($_) if $_ = $cmd->(); - } # showentry functions - addstr($val{alpha}) if exists $val{alpha}; - clrtoeol; - refresh; +END { ReadMode 0; } # restore terminal on quit + +$_->() for @{$hook{init}}; +my $redraw = 1; + +LOOP: while (1) { + if ($redraw) { + $_->() for @{$hook{refresh}}; + showstack(); + $redraw = 0; + } # refresh + + { + my $entry = showval($val{i}, $set{base}); + $entry .= $_ for map $_->(), @{$hook{postentry}}; + $entry .= $val{alpha} if exists $val{alpha}; + $_->($entry) for @{$hook{showentry}}; + } # show entry my $key = ReadKey; if ($key eq chr 27) { @@ -158,14 +145,17 @@ while (1) { $_ = $alias{$key} || $key; #if exists $alias{$key}; # command shortkeys $_ = delete $val{alpha} if $_ eq "enter" and exists $val{alpha}; # use manual command - for my $cmd (@{ $hook{precmd} }) { + for my $cmd (@{$hook{precmd}}) { next LOOP if $cmd->(); } # precmd functions last if $_ eq 'quit'; - goto DRAW if $_ eq 'refresh'; - if (exists $val{alpha} or /^\033?[A-Z]$/) { + if ($_ eq 'refresh') { + $redraw++; + } # refresh + + elsif (/^\033?[A-Z]$/ or exists $val{alpha}) { if (defined $val{i}) { unshift @stack, $val{i}; undef %val; @@ -183,11 +173,13 @@ while (1) { } # add character } # manual command entry - elsif (/^\d$/) { + elsif (/^[\da-f]$/) { + m/^[a-z]$/ and $_ = ord($_)-87; # digit>9 $val{i} = 0 unless defined $val{i}; $_ = -$_ if $val{i}<0; # substract from negative value - $val{i} = ($val{frac} and $val{frac} *= 10) ? $val{i}+$_/$val{frac} - : $val{i}*10+$_; + $val{i} = ($val{frac} and $val{frac} *= 10) + ? $val{i}+$_/$val{frac} # add digit to fraction + : $val{i}*$set{base}+$_; # add digit to integer part } # digit elsif ($_ eq '.') { $val{i} = 0 unless defined $val{i}; @@ -201,69 +193,80 @@ while (1) { $val{i} = -$val{i}; } # change sign elsif ($_ eq "back" and defined $val{i}) { - $val{i} = ($val{frac} = int $val{frac}/10) - ? int($val{i}*$val{frac})/$val{frac} : int $val{i}/10 + $val{i} = ($val{frac} and $val{frac} = int $val{frac}/10) + ? int($val{i}*$val{frac})/$val{frac} # backspace fraction digit + : int $val{i}/$set{base} # backspace digit in integer part } # backspace elsif (exists $action{$_}) { - my ($type, $cmd) = @{ $action{$_} }; - unshift @stack, $action{enter}[1]->() - if $type>0 and defined $val{i}; # auto enter + my ($type, $cmd) = @{$action{$_}}; + unshift @stack, $action{enter}[1]->() if $type>0 and defined $val{i}; # auto enter + if ($type>0 and $type>@stack) { error("insufficient stack arguments for operation"); - goto DRAW; + $redraw++; + next; } # insufficient arguments - if ($type>=0) { - $var{undo} = [@stack]; # if $_ ne 'undo'; - unshift @stack, $cmd->(splice @stack, 0, $type); - showstack(); - } # stack-modifying operation - else { - $cmd->(); - } # harmless + $_->($type) for @{$hook{preaction}}; + + # put return value(s) of stack-modifying operations (type>=0) at stack + $type<0 ? $cmd->() : unshift @stack, $cmd->(splice @stack, 0, $type); + + showstack() if $type>=-1; } # some operation else { - error("unrecognised command: ".join(' ', map ord, split //, $_)); - goto DRAW; # screen messed up + error( + "unrecognised command: " # show string or character codes + . (m/^\w*$/ ? qq{"$_"} : join ' ', map ord, split //, $_) + ); + $redraw++; # screen messed up } # error } # input loop =cut VERSION HISTORY -1.01 06-18 - start (curses, some basic commands) -1.02 06-20 - function keys select command/submenu from (sub)menu - - backspace to undo last digit -1.03 06-25 - values displayable in arbitrary base - - can enter fractions (.) and negative values (_) -1.04 08-04 14:45 - error dialog (don't mess up screen) - - manual command input using capital letters - - ^L redraws screen - pre 09-09 22:00 - overhaul in stack handling -1.05 09-10 19:45 - hp48-like drop (backspace but not editing value) - - error on insufficient arguments for command - - command backspacing - - some unit conversion (mostly lengths) from menu - - q for sq(rt) (formerly quit, now only ^D/quit) -1.06 09-15 23:10 - menu contents in module - - new commands: a?(sin|cos|tan)h, inv, !, rand - - x and v shortkeys -1.07 09-24 23:50 - numeric modifiers hardcoded instead of in action hash - - action undo: last stack alteration can be undone - - enter on no value repeats last val on stack - - new commands: sr/sr, shortkeys ( ) -1.08 09-26 22:10 - additional digits were not correctly applied to negative values - - negative numbers displayed correctly in different bases - - second undo redoes - - fixed % - - stack command (cursor up) cycles through values in stack -1.09 09-27 00:57 - all key aliases moved to module DCT::Bindings - 09-29 12:15 - number of menu items depends on screen width - 10-11 21:30 - hooks allowing for extra code at reload, showentry, and precmd - 21:50 - all menu related functions moved to menu.pm - 22:05 - unit conversion out of main program (entirely into unitconv.pm) - 10-12 01:50 - backspace becomes "back" (soft drop, like old "drop") - - normal drop command (alt+bs) removes input/stack value at once - 02:13 - $val{frac} default undefined instead of 0 +1.01 040618 - start (curses, some basic commands) +1.02 040620 - function keys select command/submenu from (sub)menu + - backspace to undo last digit +1.03 040625 - values displayable in arbitrary base + - can enter fractions (.) and negative values (_) +1.04 0408041445 - error dialog (don't mess up screen) + - manual command input using capital letters + - ^L redraws screen + 0409092200 - overhaul in stack handling +1.05 0409101945 - hp48-like drop (backspace but not editing value) + - error on insufficient arguments for command + - command backspacing + - some unit conversion (mostly lengths) from menu + - q for sq(rt) (formerly quit, now only ^D/quit) +1.06 0409152310 - menu contents in module + - new commands: a?(sin|cos|tan)h, inv, !, rand + - x and v shortkeys +1.07 0409242350 - numeric modifiers hardcoded instead of in action hash + - action undo: last stack alteration can be undone + - enter on no value repeats last val on stack + - new commands: sr/sr, shortkeys ( ) +1.08 0409262210 - additional digits were not correctly applied to negative values + - negative numbers displayed correctly in different bases + - second undo redoes + - fixed % + - stack command (cursor up) cycles through values in stack +1.09 0409270057 - all key aliases moved to module DCT::Bindings + 0409291215 - number of menu items depends on screen width + 0410112130 - hooks allowing for extra code at reload, showentry, and precmd + 2150 - all menu related functions moved to menu.pm + 2205 - unit conversion out of main program (entirely into unitconv.pm) + 0410120150 - backspace becomes "back" (soft drop, like old "drop") + - normal drop command (alt+bs) removes input/stack value at once + 0213 - $val{frac} default undefined instead of 0 +1.10 0410120245 - fixed backspace with undef fraction + 0410130020 - altered stack not redrawn after undo + 0410132200 - digits added/removed to/from integer part in correct number base + 0410142145 - allow modules to not load but without error + - display welcome at startup, also showing version and modules + 0410150000 - preaction hook; undo functionality moved to module + - only first module run of multiple with the same name + 0015 - invalid commands shown as strings instead of character codes =cut