1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-19 11:31:26 +01:00

38693: Add RPN mode to zcalc

This commit is contained in:
Peter Stephenson 2016-06-16 11:39:42 +01:00
parent f497573c80
commit 4cacf1624f
4 changed files with 160 additions and 24 deletions

View file

@ -1,5 +1,8 @@
2016-06-16 Peter Stephenson <p.stephenson@samsung.com> 2016-06-16 Peter Stephenson <p.stephenson@samsung.com>
* 38693: Doc/Zsh/contrib.yo, Functions/Misc/zcalc,
Functions/Zle/zcalc-auto-insert: Add RPN mode to zcalc.
* unposted: Doc/Zsh/params.yo: fix parentheses for getrusage(). * unposted: Doc/Zsh/params.yo: fix parentheses for getrusage().
2016-06-16 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> 2016-06-16 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>

View file

@ -2993,6 +2993,9 @@ the variable tt(ZCALC_AUTO_INSERT_PREFIX).
Hence, for example, typing `tt(PLUS()12)' followed by return adds 12 Hence, for example, typing `tt(PLUS()12)' followed by return adds 12
to the previous result. to the previous result.
If zcalc is in RPN mode (tt(-r) option) 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. When not in zcalc, the key simply inserts the symbol itself.
) )
enditem() enditem()
@ -3706,7 +3709,7 @@ sect(Mathematical Functions)
startitem() startitem()
findex(zcalc) 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 A reasonably powerful calculator based on zsh's arithmetic evaluation
facility. The syntax is similar to that of formulae in most programming facility. The syntax is similar to that of formulae in most programming
languages; see 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 point, hence for example the expression `tt(3/4)' evaluates to 0.75
rather than 0. Options must appear in separate words. 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 The prompt is configurable via the parameter tt(ZCALCPROMPT), which
undergoes standard prompt expansion. The index of the current entry is 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 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 Note that setting an output base suppresses floating point output; use
`tt([#])' to return to normal operation. `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() enditem()
See the comments in the function for a few extra tips. See the comments in the function for a few extra tips.

View file

@ -96,6 +96,20 @@
emulate -L zsh emulate -L zsh
setopt extendedglob 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. # For testing in ZLE functions.
local ZCALC_ACTIVE=1 local ZCALC_ACTIVE=1
@ -103,15 +117,20 @@ local ZCALC_ACTIVE=1
# begin with _. # begin with _.
local line ans base defbase forms match mbegin mend psvar optlist opt arg local line ans base defbase forms match mbegin mend psvar optlist opt arg
local compcontext="-zcalc-line-" local compcontext="-zcalc-line-"
integer num outdigits outform=1 expression_mode integer num outdigits outform=1 expression_mode rpn_mode matched show_stack i
local -a expressions integer max_stack
local -a expressions stack match mbegin mend
# We use our own history file with an automatic pop on exit. # We use our own history file with an automatic pop on exit.
history -ap "${ZDOTDIR:-$HOME}/.zcalc_history" history -ap "${ZDOTDIR:-$HOME}/.zcalc_history"
forms=( '%2$g' '%.*g' '%.*f' '%.*E' '') 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 autoload -Uz zmathfuncdef
if (( ! ${+ZCALCPROMPT} )); then if (( ! ${+ZCALCPROMPT} )); then
@ -127,7 +146,7 @@ if [[ -f "${ZDOTDIR:-$HOME}/.zcalcrc" ]]; then
fi fi
# Process command line # Process command line
while [[ -n $1 && $1 = -(|[#-]*|f|e) ]]; do while [[ -n $1 && $1 = -(|[#-]*|f|e|r(<->|)) ]]; do
optlist=${1[2,-1]} optlist=${1[2,-1]}
shift shift
[[ $optlist = (|-) ]] && break [[ $optlist = (|-) ]] && break
@ -158,6 +177,14 @@ while [[ -n $1 && $1 = -(|[#-]*|f|e) ]]; do
(e) # Arguments are expressions (e) # Arguments are expressions
(( expression_mode = 1 )); (( 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 esac
done done
done done
@ -281,30 +308,95 @@ while (( expression_mode )) ||
continue 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 line=${${line##[[:blank:]]##}%%[[:blank:]]##}
# point or integer --- we don't know till after the evaluation, and if (( rpn_mode )); then
# arrays always store scalars anyway. matched=1
# case $line in
# 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. if (( ${#stack} < 1 )); then
eval "ans=\$(( $line ))" print -r -- "${line}: not enough values on stack" >&2
# on error $ans is not set; let user re-edit line line=
[[ -n $ans ]] || continue 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 argv[num++]=$ans
psvar[1]=$num psvar[1]=$num
stack=($ans $stack)
;; ;;
esac esac
if [[ -n $base ]]; then if (( show_stack )); then
print -- $(( $base $ans )) (( max_stack = (show_stack > ${#stack}) ? ${#stack} : show_stack ))
elif [[ $ans = *.* ]] || (( outdigits )); then for (( i = max_stack; i > 0; i-- )); do
if [[ -z $forms[outform] ]]; then printf "%3d: " $i
print -- $(( $ans )) zcalc_show_value ${stack[i]}
else done
printf "$forms[outform]\n" $outdigits $ans
fi
else else
printf "%d\n" $ans zcalc_show_value $ans
fi fi
line= line=
done done

View file

@ -1,6 +1,7 @@
# Bind to a binary operator keystroke for use with zcalc # 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 if [[ $CURSOR -eq 0 || $LBUFFER[-1] = "(" ]]; then
LBUFFER+=${ZCALC_AUTO_INSERT_PREFIX:-"ans "} LBUFFER+=${ZCALC_AUTO_INSERT_PREFIX:-"ans "}
fi fi