Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: zcalc RPN part 2
- X-seq: zsh-workers 38736
- From: Peter Stephenson <p.stephenson@xxxxxxxxxxx>
- To: Zsh Hackers' List <zsh-workers@xxxxxxx>
- Subject: PATCH: zcalc RPN part 2
- Date: Tue, 21 Jun 2016 16:28:28 +0100
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
- Organization: Samsung Cambridge Solution Centre
Second batch of tweaks for RPN in zcalc.
You can now do stuff like
:f cube $1*1*$1 # define a function
32 # push 32 onto stack
cube # call the function you've defined on 32
(the only new element here is it recognises "cube" as a user defined
function taking one argument. You can still do "cube(32)"; RPN mode
tries to change the basics as little as possible.)
pop # discard top element of stack
< # same
<foo # same but also assign to "foo"
xy # exchange top two elements of stack
It would be tempting not to use names at all for this and just have
"<" for pop and maybe "<>" for exchange. However, German keyboard users
sometimes complain about that sort of thing as some of the more squiggly
characters are a bit hidden.
Also added some missing standard binary operators (i.e. ones that worked
in a normal expression but didn't work to combine the top two elements
of the stack).
Also in standard output mode ensure a trailing "." on a number doesn't
get omitted in the output, which was confusing. This is a bit of a hack.
Next project is to do something about
# TODO: make local variables that shouldn't be visible in expressions
# begin with _.
which has reached the pain threshold. I won't bother posting the
diff except perhaps for the documentation.
pws
diff --git a/Completion/Zsh/Type/_module_math_func b/Completion/Zsh/Type/_module_math_func
index 4df8d97..6be9c00 100644
--- a/Completion/Zsh/Type/_module_math_func
+++ b/Completion/Zsh/Type/_module_math_func
@@ -6,4 +6,4 @@ local -a funcs
funcs=(${${${(f)"$(zmodload -Fl zsh/mathfunc 2>/dev/null)"}:#^+f:*}##+f:})
_wanted module-math-functions expl 'math function from zsh/mathfunc' \
- compadd -S '(' "$@" -a funcs
+ compadd -S '(' -q "$@" -a funcs
diff --git a/Completion/Zsh/Type/_user_math_func b/Completion/Zsh/Type/_user_math_func
index 16774f7..35a49d5 100644
--- a/Completion/Zsh/Type/_user_math_func
+++ b/Completion/Zsh/Type/_user_math_func
@@ -6,4 +6,4 @@ local -a funcs
funcs=(${${${(f)"$(functions -M)"}##functions -M }%% *})
_wanted user-math-functions expl 'user math function' \
- compadd -S '(' "$@" -a funcs
+ compadd -S '(' -q "$@" -a funcs
diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index b9c1c0a..c875c95 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -3806,10 +3806,23 @@ 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)(
+item(Duplication: tt(=))(
The pseudo-operator tt(=) causes the most recent element of
the stack to be duplicated onto the stack.
)
+item(tt(pop))(
+The pseudo-function tt(pop) causes the most recent element of
+the stack to be popped. A `tt(<)' on its own has the same effect.
+)
+item(tt(<)var(ident))(
+The expression tt(<) followed (with no space) by a shell identifier
+causes the most recent element of the stack to be popped and
+assigned to the identifier.
+)
+item(Exchange: tt(xy))(
+The pseudo-function tt(xy) causes the most recent two elements of
+the stack to be exchanged.
+)
enditem()
The prompt is configurable via the parameter tt(ZCALCPROMPT), which
@@ -3872,7 +3885,13 @@ Note that tt(zcalc) takes care of all quoting. Hence for example:
example(:f cube $1 * $1 * $1)
-defines a function to cube the sole argument.
+defines a function to cube the sole argument. Functions so defined, or
+indeed any functions defined directly or indirectly using tt(functions
+-M), are available to execute by typing only the name on the line in RPN
+mode; this pops the appropriate number of arguments off the stack
+to pass to the function, i.e. 1 in the case of the example tt(cube)
+function. If there are optional arguments only the mandatory
+arguments are supplied by this means.
)
item(tt([#)var(base)tt(]))(
This is not a special command, rather part of normal arithmetic
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
index eb240b2..fa1a8f6 100644
--- a/Functions/Misc/zcalc
+++ b/Functions/Misc/zcalc
@@ -100,7 +100,8 @@ zcalc_show_value() {
if [[ -n $base ]]; then
print -- $(( $base $1 ))
elif [[ $1 = *.* ]] || (( outdigits )); then
- if [[ -z $forms[outform] ]]; then
+ # With normal output, ensure trailing "." doesn't get lost.
+ if [[ -z $forms[outform] || ($outform -eq 1 && $1 = *.) ]]; then
print -- $(( $1 ))
else
printf "$forms[outform]\n" $outdigits $1
@@ -115,10 +116,10 @@ local ZCALC_ACTIVE=1
# TODO: make local variables that shouldn't be visible in expressions
# 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 tmp
local compcontext="-zcalc-line-"
-integer num outdigits outform=1 expression_mode rpn_mode matched show_stack i
-integer max_stack
+integer num outdigits outform=1 expression_mode rpn_mode matched show_stack i n
+integer max_stack push
local -a expressions stack match mbegin mend
# We use our own history file with an automatic pop on exit.
@@ -131,6 +132,13 @@ if zmodload -i zsh/mathfunc 2>/dev/null; then
zmodload -P mathfuncs -FL zsh/mathfunc
mathfuncs="("${(j.|.)${mathfuncs##f:}}")"
fi
+local -A userfuncs
+for line in ${(f)"$(functions -M)"}; do
+ match=(${=line})
+ # get minimum number of arguments
+ userfuncs[${match[3]}]=${match[4]}
+done
+line=
autoload -Uz zmathfuncdef
if (( ! ${+ZCALCPROMPT} )); then
@@ -298,6 +306,7 @@ while (( expression_mode )) ||
((function|:f(unc(tion|)|))[[:blank:]]##(#b)([^[:blank:]]##)(|[[:blank:]]##([^[:blank:]]*)))
zmathfuncdef $match[1] $match[3]
+ userfuncs[$match[1]]=${$(functions -Mm $match[1])[4]}
line=
continue
;;
@@ -318,19 +327,38 @@ while (( expression_mode )) ||
(*)
line=${${line##[[:blank:]]##}%%[[:blank:]]##}
- if (( rpn_mode )); then
+ if [[ rpn_mode -ne 0 && $line != '' ]]; then
+ push=1
matched=1
case $line in
- (=)
+ (\=|pop|\<[[:IDENT:]]#)
if (( ${#stack} < 1 )); then
print -r -- "${line}: not enough values on stack" >&2
line=
continue
fi
- ans=${stack[1]}
+ case $line in
+ (=)
+ ans=${stack[1]}
+ ;;
+ (pop|\<)
+ push=0
+ shift stack
+ ;;
+ (\<[[:IDENT:]]##)
+ (( ${line##\<} = ${stack[1]} ))
+ push=0
+ shift stack
+ ;;
+ (*)
+ print "BUG in special RPN functions" >&2
+ line=
+ continue
+ ;;
+ esac
;;
- (+|-|\^|\||\&|\*|\*\*|/)
+ (+|-|\^|\||\&|\*|/|\*\*|\>\>|\<\</)
# Operators with two arguments
if (( ${#stack} < 2 )); then
print -r -- "${line}: not enough values on stack" >&2
@@ -341,15 +369,22 @@ while (( expression_mode )) ||
shift 2 stack
;;
- (ldexp|jn|yn|scalb)
+ (ldexp|jn|yn|scalb|xy)
# 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
+ if [[ $line = xy ]]; then
+ tmp=${stack[1]}
+ stack[1]=${stack[2]}
+ stack[2]=$tmp
+ push=0
+ else
+ eval "(( ans = ${line}(\${stack[2]},\${stack[1]}) ))"
+ shift 2 stack
+ fi
;;
(${~mathfuncs})
@@ -365,6 +400,25 @@ while (( expression_mode )) ||
shift stack
;;
+ (${(kj.|.)~userfuncs})
+ # Get minimum number of arguments to user function
+ n=${userfuncs[$line]}
+ if (( ${#stack} < n )); then
+ print -r -- "${line}: not enough vlaues ($n) on stack" >&2
+ line=
+ continue
+ fi
+ line+="("
+ # least recent elements on stack are earlier arguments
+ for (( i = n; i > 0; i-- )); do
+ line+=${stack[i]}
+ (( i > 1 )) && line+=","
+ done
+ line+=")"
+ shift $n stack
+ eval "(( ans = $line ))"
+ ;;
+
(*)
# Treat as expression evaluating to new value to go on stack.
matched=0
@@ -386,7 +440,7 @@ while (( expression_mode )) ||
fi
argv[num++]=$ans
psvar[1]=$num
- stack=($ans $stack)
+ (( push )) && stack=($ans $stack)
;;
esac
if (( show_stack )); then
Messages sorted by:
Reverse Date,
Date,
Thread,
Author