about summary refs log tree commit diff
path: root/Functions/Misc/zcalc
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2016-06-16 11:39:42 +0100
committerPeter Stephenson <pws@zsh.org>2016-06-16 11:39:42 +0100
commit4cacf1624f0393c43a9a2c3259957b8d78eb7609 (patch)
tree02c8f0a91e37e26da61ba02391784e81388138ad /Functions/Misc/zcalc
parentf497573c80eff3ee0434867e24c1a4dc67ad80ee (diff)
downloadzsh-4cacf1624f0393c43a9a2c3259957b8d78eb7609.tar.gz
zsh-4cacf1624f0393c43a9a2c3259957b8d78eb7609.tar.xz
zsh-4cacf1624f0393c43a9a2c3259957b8d78eb7609.zip
38693: Add RPN mode to zcalc
Diffstat (limited to 'Functions/Misc/zcalc')
-rw-r--r--Functions/Misc/zcalc136
1 files changed, 114 insertions, 22 deletions
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
index 857007a94..eb240b2ec 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