mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-01 05:16:05 +01:00
38693: Add RPN mode to zcalc
This commit is contained in:
parent
f497573c80
commit
4cacf1624f
4 changed files with 160 additions and 24 deletions
|
@ -1,5 +1,8 @@
|
|||
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().
|
||||
|
||||
2016-06-16 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
|
||||
|
|
|
@ -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) 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.
|
||||
)
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue