From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23762 invoked by alias); 21 Jun 2016 15:28:36 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 38736 Received: (qmail 24225 invoked from network); 21 Jun 2016 15:28:34 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,HDRS_LCASE, T_MANY_HDRS_LCASE autolearn=ham autolearn_force=no version=3.4.1 X-AuditID: cbfec7f4-f796c6d000001486-b7-57695d1fd02b Date: Tue, 21 Jun 2016 16:28:28 +0100 From: Peter Stephenson To: Zsh Hackers' List Subject: PATCH: zcalc RPN part 2 Message-id: <20160621162828.72371587@pwslap01u.europe.root.pri> Organization: Samsung Cambridge Solution Centre X-Mailer: Claws Mail 3.7.9 (GTK+ 2.22.0; i386-redhat-linux-gnu) MIME-version: 1.0 Content-type: text/plain; charset=US-ASCII Content-transfer-encoding: 7bit X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrGLMWRmVeSWpSXmKPExsVy+t/xy7rysZnhBlfXy1kcbH7I5MDoserg B6YAxigum5TUnMyy1CJ9uwSujAsNC9kK/jlVXG/czNrAuNuoi5GTQ0LARKLvZhczhC0mceHe erYuRi4OIYGljBIbmi5AOTOYJL6vugzlnGOUOPdtJpRzllHi973XjF2MHBwsAqoSTz7KgYxi EzCUmLppNlhYREBbov2jGEhYWEBBYtmiHawgYV4Be4krMypAwvwC+hJX/35igjjCXmLmlTOM IDavgKDEj8n3WEBsZgEtic3bmlghbHmJzWvegh0tJKAucePubvYJjIKzkLTMQtIyC0nLAkbm VYyiqaXJBcVJ6bmGesWJucWleel6yfm5mxghofllB+PiY1aHGAU4GJV4eBX0M8KFWBPLiitz DzFKcDArifAGhWeGC/GmJFZWpRblxxeV5qQWH2KU5mBREuedu+t9iJBAemJJanZqakFqEUyW iYNTqoExPq2L66Bqtd/iCb2xdwtP7fPjfZyV+2DDHmkmH+9Dj8wePdHdHMgwbetigQm65ovX 3nq6/k/R9XaZcx3hkkc6EjROt77adbXFI/Kx9p1Hbdlu8ySy7+zpuuymEmqaK6/0UvbsySdN mUEfA/rC5XW/egcVzr362zH9/8XtlpWS7zieS8bXVu1SYinOSDTUYi4qTgQAHhx+ekkCAAA= 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 " 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 ;; - (+|-|\^|\||\&|\*|\*\*|/) + (+|-|\^|\||\&|\*|/|\*\*|\>\>|\<\&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