zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: RPN mode for zcalc
@ 2016-06-16  9:00 Peter Stephenson
  2016-06-16 10:40 ` Peter Stephenson
  2016-06-16 15:40 ` Christian Neukirchen
  0 siblings, 2 replies; 6+ messages in thread
From: Peter Stephenson @ 2016-06-16  9:00 UTC (permalink / raw)
  To: Zsh Hackers' List

This adds an RPN mode for zcalc.  (Because it does, that's why.)  See
the documentation.

Simple example of "zcalc -r4", which shows you up to the most recent
four elements on the stack as you proceed (the full stack is maintined
internally):

1> 2             <- User input
  1: 2           <- Pushed onto stack
2> 14            <- More input
  2: 2           <- )
  1: 14          <- ) Now two entries on stack
3> +             <- User input recognised as operator
  1: 16          <- Top two entries of stack popped and answer pushed
4> sqrt          <- User input recognised as function
  1: 4           <- Replaces top of stack

pws

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 2164c04..4db5957 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -2993,6 +2993,9 @@ the variable tt(ZCALC_AUTO_INSERT_PREFIX).
 Hence, for example, typing `tt(PLUS()12)' followed by return adds 12
 to the previous result.
 
+If zcalc is in RPN mode tt(-r) the effect of this binding is
+automatically suppressed as operators alone on a line are meaningful.
+
 When not in zcalc, the key simply inserts the symbol itself.
 )
 enditem()
@@ -3706,7 +3709,7 @@ sect(Mathematical Functions)
 
 startitem()
 findex(zcalc)
-item(tt(zcalc) [ tt(-ef) ] [ var(expression) ... ])(
+item(tt(zcalc) [ tt(-erf) ] [ var(expression) ... ])(
 A reasonably powerful calculator based on zsh's arithmetic evaluation
 facility.  The syntax is similar to that of formulae in most programming
 languages; see
@@ -3772,6 +3775,39 @@ If the option `tt(-f)' is set, all numbers are treated as floating
 point, hence for example the expression `tt(3/4)' evaluates to 0.75
 rather than 0.  Options must appear in separate words.
 
+If the option `tt(-r)' is set, RPN (Reverse Polish Notation) mode is
+entered.  This has various additional properties:
+startitem()
+item(Stack)(
+Evaluated values are maintained in a stack; this is contained in
+an array named tt(stack) with the most recent value in tt(${stack[1]}).
+)
+item(Operators and functions)(
+If the line entered matches an operator (tt(+), tt(-), tt(*),
+tt(/), tt(**), tt(^), tt(|) or tt(&)) or a function supplied by the
+tt(zsh/mathfunc) library, the bottom element or elements of the stack
+are popped to use as the argument or arguments.  The higher elements
+of stack (least recent) are used as earlier arguments.  The result is
+then pushed into tt(${stack[1]}).
+)
+item(Expressions)(
+Other expressions are evaluated normally, printed, and added to the
+stack as numeric values.  The syntax within expressions on a single line
+is normal shell arithmetic (not RPN).
+)
+item(Stack listing)(
+If an integer follows the option tt(-r) with no space, then
+on every evaluation that many elements of the stack, where available,
+are printed instead of just the most recent result.  Hence, for example,
+tt(zcalc -r4) shows tt($stack[4]) to tt($stack[1]) each time results
+are printed.
+)
+item(Duplication)(
+The pseudo-operator tt(=) causes the most recent element of
+the stack to be duplicated onto the stack.
+)
+enditem()
+
 The prompt is configurable via the parameter tt(ZCALCPROMPT), which
 undergoes standard prompt expansion.  The index of the current entry is
 stored locally in the first element of the array tt(psvar), which can be
@@ -3844,6 +3880,10 @@ always specified in decimal. `tt([#])' restores the normal output format.
 Note that setting an output base suppresses floating point output; use
 `tt([#])' to return to normal operation.
 )
+item(tt($)var(var))(
+Print out the value of var literally; does not affect the calculation.
+To use the value of var, omit the leading `tt($)'.
+)
 enditem()
 
 See the comments in the function for a few extra tips.
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
index 857007a..eb240b2 100644
--- a/Functions/Misc/zcalc
+++ b/Functions/Misc/zcalc
@@ -96,6 +96,20 @@
 emulate -L zsh
 setopt extendedglob
 
+zcalc_show_value() {
+  if [[ -n $base ]]; then
+    print -- $(( $base $1 ))
+  elif [[ $1 = *.* ]] || (( outdigits )); then
+    if [[ -z $forms[outform] ]]; then
+      print -- $(( $1 ))
+    else
+      printf "$forms[outform]\n" $outdigits $1
+    fi
+  else
+    printf "%d\n" $1
+  fi
+}
+
 # For testing in ZLE functions.
 local ZCALC_ACTIVE=1
 
@@ -103,15 +117,20 @@ local ZCALC_ACTIVE=1
 # begin with _.
 local line ans base defbase forms match mbegin mend psvar optlist opt arg
 local compcontext="-zcalc-line-"
-integer num outdigits outform=1 expression_mode
-local -a expressions
+integer num outdigits outform=1 expression_mode rpn_mode matched show_stack i
+integer max_stack
+local -a expressions stack match mbegin mend
 
 # We use our own history file with an automatic pop on exit.
 history -ap "${ZDOTDIR:-$HOME}/.zcalc_history"
 
 forms=( '%2$g' '%.*g' '%.*f' '%.*E' '')
 
-zmodload -i zsh/mathfunc 2>/dev/null
+local mathfuncs
+if zmodload -i zsh/mathfunc 2>/dev/null; then
+  zmodload -P mathfuncs -FL zsh/mathfunc
+  mathfuncs="("${(j.|.)${mathfuncs##f:}}")"
+fi
 autoload -Uz zmathfuncdef
 
 if (( ! ${+ZCALCPROMPT} )); then
@@ -127,7 +146,7 @@ if [[ -f "${ZDOTDIR:-$HOME}/.zcalcrc" ]]; then
 fi
 
 # Process command line
-while [[ -n $1 && $1 = -(|[#-]*|f|e) ]]; do
+while [[ -n $1 && $1 = -(|[#-]*|f|e|r(<->|)) ]]; do
   optlist=${1[2,-1]}
   shift
   [[ $optlist = (|-) ]] && break
@@ -158,6 +177,14 @@ while [[ -n $1 && $1 = -(|[#-]*|f|e) ]]; do
         (e) # Arguments are expressions
 	    (( expression_mode = 1 ));
 	    ;;
+        (r) # RPN mode.
+	    (( rpn_mode = 1 ))
+	    ZCALC_ACTIVE=rpn
+	    if [[ $optlist = (#b)(<->)* ]]; then
+	       (( show_stack = ${match[1]} ))
+               optlist=${optlist[${#match[1]}+1,-2]}
+	    fi
+	    ;;
     esac
   done
 done
@@ -281,30 +308,95 @@ while (( expression_mode )) ||
     continue
     ;;
 
+    (\$[[:IDENT:]]##)
+    # Display only, no calculation
+    line=${line##\$}
+    print -r -- ${(P)line}
+    line=
+    continue
+    ;;
+
     (*)
-      # Latest value is stored as a string, because it might be floating
-      # point or integer --- we don't know till after the evaluation, and
-      # arrays always store scalars anyway.
-      #
-      # Since it's a string, we'd better make sure we know which
-      # base it's in, so don't change that until we actually print it.
-      eval "ans=\$(( $line ))"
-      # on error $ans is not set; let user re-edit line
-      [[ -n $ans ]] || continue
+      line=${${line##[[:blank:]]##}%%[[:blank:]]##}
+      if (( rpn_mode )); then
+	matched=1
+	case $line in
+	  (=)
+	  if (( ${#stack} < 1 )); then
+	    print -r -- "${line}: not enough values on stack" >&2
+	    line=
+	    continue
+	  fi
+	  ans=${stack[1]}
+	  ;;
+
+	  (+|-|\^|\||\&|\*|\*\*|/)
+	  # Operators with two arguments
+	  if (( ${#stack} < 2 )); then
+	    print -r -- "${line}: not enough values on stack" >&2
+	    line=
+	    continue
+	  fi
+	  eval "(( ans = \${stack[2]} $line \${stack[1]} ))"
+	  shift 2 stack
+	  ;;
+
+	  (ldexp|jn|yn|scalb)
+	  # Functions with two arguments
+	  if (( ${#stack} < 2 )); then
+	    print -r -- "${line}: not enough values on stack" >&2
+	    line=
+	    continue
+	  fi
+	  eval "(( ans = ${line}(\${stack[2]},\${stack[1]}) ))"
+	  shift 2 stack
+	  ;;
+
+	  (${~mathfuncs})
+	  # Functions with a single argument.
+	  # This is actually a superset, but we should have matched
+	  # any that shouldn't be in it in previous cases.
+	  if (( ${#stack} < 1 )); then
+	    print -r -- "${line}: not enough values on stack" >&2
+	    line=
+	    continue
+	  fi
+	  eval "(( ans = ${line}(\${stack[1]}) ))"
+	  shift stack
+	  ;;
+
+	  (*)
+	  # Treat as expression evaluating to new value to go on stack.
+	  matched=0
+	  ;;
+	esac
+      else
+	matched=0
+      fi
+      if (( ! matched )); then
+	# Latest value is stored` as a string, because it might be floating
+	# point or integer --- we don't know till after the evaluation, and
+	# arrays always store scalars anyway.
+	#
+	# Since it's a string, we'd better make sure we know which
+	# base it's in, so don't change that until we actually print it.
+	eval "ans=\$(( $line ))"
+	# on error $ans is not set; let user re-edit line
+	[[ -n $ans ]] || continue
+      fi
       argv[num++]=$ans
       psvar[1]=$num
+      stack=($ans $stack)
     ;;
   esac
-  if [[ -n $base ]]; then
-    print -- $(( $base $ans ))
-  elif [[ $ans = *.* ]] || (( outdigits )); then
-    if [[ -z $forms[outform] ]]; then
-      print -- $(( $ans ))
-    else
-      printf "$forms[outform]\n" $outdigits $ans
-    fi
+  if (( show_stack )); then
+    (( max_stack = (show_stack > ${#stack}) ? ${#stack} : show_stack ))
+    for (( i = max_stack; i > 0; i-- )); do
+      printf "%3d: " $i
+      zcalc_show_value ${stack[i]}
+    done
   else
-    printf "%d\n" $ans
+    zcalc_show_value $ans
   fi
   line=
 done
diff --git a/Functions/Zle/zcalc-auto-insert b/Functions/Zle/zcalc-auto-insert
index c9a5c88..e1affd1 100644
--- a/Functions/Zle/zcalc-auto-insert
+++ b/Functions/Zle/zcalc-auto-insert
@@ -1,6 +1,7 @@
 # Bind to a binary operator keystroke for use with zcalc
+# Not useful in RPN mode.
 
-if [[ -n $ZCALC_ACTIVE ]]; then
+if [[ -n $ZCALC_ACTIVE && $ZCALC_ACTIVE != rpn ]]; then
   if [[ $CURSOR -eq 0 || $LBUFFER[-1] = "(" ]]; then
     LBUFFER+=${ZCALC_AUTO_INSERT_PREFIX:-"ans "}
   fi


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: RPN mode for zcalc
  2016-06-16  9:00 PATCH: RPN mode for zcalc Peter Stephenson
@ 2016-06-16 10:40 ` Peter Stephenson
  2016-06-16 15:40 ` Christian Neukirchen
  1 sibling, 0 replies; 6+ messages in thread
From: Peter Stephenson @ 2016-06-16 10:40 UTC (permalink / raw)
  To: Zsh Hackers' List

On Thu, 16 Jun 2016 10:00:06 +0100
Peter Stephenson <p.stephenson@samsung.com> wrote:
> This adds an RPN mode for zcalc.  (Because it does, that's why.)  See
> the documentation.

I've pushed this to try out, but it's not set in stone.

pws


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: RPN mode for zcalc
  2016-06-16  9:00 PATCH: RPN mode for zcalc Peter Stephenson
  2016-06-16 10:40 ` Peter Stephenson
@ 2016-06-16 15:40 ` Christian Neukirchen
  2016-06-16 16:09   ` Peter Stephenson
  1 sibling, 1 reply; 6+ messages in thread
From: Christian Neukirchen @ 2016-06-16 15:40 UTC (permalink / raw)
  To: zsh-workers

Peter Stephenson <p.stephenson@samsung.com> writes:

> This adds an RPN mode for zcalc.  (Because it does, that's why.)  See
> the documentation.

It's a nice idea, but it would be even better if space worked as ENTER too,
so "5 4 +" returns 9.

And perhaps an operation to exchange the top two elements.

-- 
Christian Neukirchen  <chneukirchen@gmail.com>  http://chneukirchen.org


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: RPN mode for zcalc
  2016-06-16 15:40 ` Christian Neukirchen
@ 2016-06-16 16:09   ` Peter Stephenson
  2016-06-17  9:26     ` Christian Neukirchen
  0 siblings, 1 reply; 6+ messages in thread
From: Peter Stephenson @ 2016-06-16 16:09 UTC (permalink / raw)
  To: zsh-workers

On Thu, 16 Jun 2016 17:40:48 +0200
Christian Neukirchen <chneukirchen@gmail.com> wrote:
> Peter Stephenson <p.stephenson@samsung.com> writes:
> 
> > This adds an RPN mode for zcalc.  (Because it does, that's why.)  See
> > the documentation.
> 
> It's a nice idea, but it would be even better if space worked as ENTER too,
> so "5 4 +" returns 9.

That's another function entirely, based on keystrokes rather than lines
containing expressions; it doesn't fit into zcalc.

Of course, if you want to write one, feel free.

> And perhaps an operation to exchange the top two elements.

I'll probably add some things of this sort.

pws


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: RPN mode for zcalc
  2016-06-16 16:09   ` Peter Stephenson
@ 2016-06-17  9:26     ` Christian Neukirchen
  2016-06-17 10:02       ` Peter Stephenson
  0 siblings, 1 reply; 6+ messages in thread
From: Christian Neukirchen @ 2016-06-17  9:26 UTC (permalink / raw)
  To: zsh-workers

Peter Stephenson <p.stephenson@samsung.com> writes:

> On Thu, 16 Jun 2016 17:40:48 +0200
> Christian Neukirchen <chneukirchen@gmail.com> wrote:
>> Peter Stephenson <p.stephenson@samsung.com> writes:
>> 
>> > This adds an RPN mode for zcalc.  (Because it does, that's why.)  See
>> > the documentation.
>> 
>> It's a nice idea, but it would be even better if space worked as ENTER too,
>> so "5 4 +" returns 9.
>
> That's another function entirely, based on keystrokes rather than lines
> containing expressions; it doesn't fit into zcalc.
>
> Of course, if you want to write one, feel free.

I don't mean the space to update the display or anything, just
tokenize the input first?

I mean, I can type 6+7*8<RET> now, why shouldn't I be able to type
6 7 8 * + <RET>

>> And perhaps an operation to exchange the top two elements.
>
> I'll probably add some things of this sort.

Thanks,
-- 
Christian Neukirchen  <chneukirchen@gmail.com>  http://chneukirchen.org


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: RPN mode for zcalc
  2016-06-17  9:26     ` Christian Neukirchen
@ 2016-06-17 10:02       ` Peter Stephenson
  0 siblings, 0 replies; 6+ messages in thread
From: Peter Stephenson @ 2016-06-17 10:02 UTC (permalink / raw)
  To: zsh-workers

On Fri, 17 Jun 2016 11:26:42 +0200
Christian Neukirchen <chneukirchen@gmail.com> wrote:
> I mean, I can type 6+7*8<RET> now, why shouldn't I be able to type
> 6 7 8 * + <RET>

You can still type 6+7*8; expressions within the line work as
before and you get the result on the stack.  The overall result is
identical.

pws


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2016-06-17 10:12 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-16  9:00 PATCH: RPN mode for zcalc Peter Stephenson
2016-06-16 10:40 ` Peter Stephenson
2016-06-16 15:40 ` Christian Neukirchen
2016-06-16 16:09   ` Peter Stephenson
2016-06-17  9:26     ` Christian Neukirchen
2016-06-17 10:02       ` Peter Stephenson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).