From 4cacf1624f0393c43a9a2c3259957b8d78eb7609 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 16 Jun 2016 11:39:42 +0100 Subject: 38693: Add RPN mode to zcalc --- Functions/Misc/zcalc | 136 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 22 deletions(-) (limited to 'Functions/Misc/zcalc') 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 -- cgit v1.2.3 From 5103c85abb239a12a3d29da9b41515cd4af19a9f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 21 Jun 2016 16:34:55 +0100 Subject: 38736: various RPN mode enhancements for zcalc --- ChangeLog | 4 ++ Completion/Zsh/Type/_module_math_func | 2 +- Completion/Zsh/Type/_user_math_func | 2 +- Doc/Zsh/contrib.yo | 23 ++++++++++- Functions/Misc/zcalc | 78 +++++++++++++++++++++++++++++------ 5 files changed, 93 insertions(+), 16 deletions(-) (limited to 'Functions/Misc/zcalc') diff --git a/ChangeLog b/ChangeLog index fd58e7aa6..4ca92814a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2016-06-21 Peter Stephenson + * 38736: Completion/Zsh/Type/_module_math_func, + Completion/Zsh/Type/_user_math_func, Doc/Zsh/contrib.yo, + Functions/Misc/zcalc: various RPN mode enhancments for zcalc. + * 38734: Src/loop.c, Test/A01grammar.ztst: fix final case clauses terminating with ;&. diff --git a/Completion/Zsh/Type/_module_math_func b/Completion/Zsh/Type/_module_math_func index 4df8d9714..6be9c006a 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 16774f70b..35a49d50e 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 b9c1c0a80..c875c95da 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 eb240b2ec..fa1a8f600 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 -- cgit v1.2.3 From 26c01f57113cc76e20ec562ffcec60a1ab9f8b1e Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 21 Jun 2016 17:26:35 +0100 Subject: 38737: clean up zcalc variables. Undocumented variables now start with "_". --- ChangeLog | 3 + Doc/Zsh/contrib.yo | 17 ++- Functions/Misc/zcalc | 289 ++++++++++++++++++++++++++------------------------- 3 files changed, 163 insertions(+), 146 deletions(-) (limited to 'Functions/Misc/zcalc') diff --git a/ChangeLog b/ChangeLog index 4ca92814a..b95b0fe31 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2016-06-21 Peter Stephenson + * 38737: Functions/Misc/zcalc, Doc/Zsh/contrib.yo: document some + zcalc variable usage and make other variables start with "_". + * 38736: Completion/Zsh/Type/_module_math_func, Completion/Zsh/Type/_user_math_func, Doc/Zsh/contrib.yo, Functions/Misc/zcalc: various RPN mode enhancments for zcalc. diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index c875c95da..53ae96dad 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -3764,8 +3764,14 @@ first few positional parameters. A visual indication of this is given when the calculator starts. The constants tt(PI) (3.14159...) and tt(E) (2.71828...) are provided. -Parameter assignment is possible, but note that all parameters will be put -into the global namespace. +Parameter assignment is possible, but note that all parameters will be +put into the global namespace unless the tt(:local) special command is +used. The function creates local variables whose names start with +tt(_), so users should avoid doing so. The variables tt(ans) (the last +answer) and tt(stack) (the stack in RPN mode) may be referred to +directly; tt(stack) is an array but elements of it are numeric. Various +other special variables are used locally with their standard meaning, +for example tt(compcontext), tt(match), tt(mbegin), tt(mend), tt(psvar). The output base can be initialised by passing the option `tt(-#)var(base)', for example `tt(zcalc -#16)' (the `tt(#)' may have to be quoted, depending @@ -3831,6 +3837,10 @@ stored locally in the first element of the array tt(psvar), which can be referred to in tt(ZCALCPROMPT) as `tt(%1v)'. The default prompt is `tt(%1v> )'. +The variable tt(ZCALC_ACTIVE) is set within the function and can +be tested by nested functions; it has the value tt(rpn) if RPN mode is +active, else 1. + A few special commands are available; these are introduced by a colon. For backward compatibility, the colon may be omitted for certain commands. Completion is available if tt(compinit) has been run. @@ -3870,8 +3880,7 @@ is executed in the context of the function, i.e. with local variables. Space is optional after tt(:!). ) item(tt(:local) var(arg) ...)( -Declare variables local to the function. Note that certain variables -are used by the function for its own purposes. Other variables +Declare variables local to the function. Other variables may be used, too, but they will be taken from or put into the global scope. ) diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc index fa1a8f600..86b1e4a5b 100644 --- a/Functions/Misc/zcalc +++ b/Functions/Misc/zcalc @@ -97,14 +97,14 @@ emulate -L zsh setopt extendedglob zcalc_show_value() { - if [[ -n $base ]]; then - print -- $(( $base $1 )) - elif [[ $1 = *.* ]] || (( outdigits )); then + if [[ -n $_base ]]; then + print -- $(( $_base $1 )) + elif [[ $1 = *.* ]] || (( _outdigits )); then # With normal output, ensure trailing "." doesn't get lost. - if [[ -z $forms[outform] || ($outform -eq 1 && $1 = *.) ]]; then + if [[ -z $_forms[_outform] || ($_outform -eq 1 && $1 = *.) ]]; then print -- $(( $1 )) else - printf "$forms[outform]\n" $outdigits $1 + printf "$_forms[_outform]\n" $_outdigits $1 fi else printf "%d\n" $1 @@ -116,29 +116,31 @@ 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 tmp +local _line ans _base _defbase _forms match mbegin mend +local psvar _optlist _opt _arg _tmp local compcontext="-zcalc-line-" -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 +integer _num _outdigits _outform=1 _expression_mode +integer _rpn_mode _matched _show_stack _i _n +integer _max_stack _push +local -a _expressions stack # We use our own history file with an automatic pop on exit. history -ap "${ZDOTDIR:-$HOME}/.zcalc_history" -forms=( '%2$g' '%.*g' '%.*f' '%.*E' '') +_forms=( '%2$g' '%.*g' '%.*f' '%.*E' '') -local mathfuncs +local _mathfuncs if zmodload -i zsh/mathfunc 2>/dev/null; then - zmodload -P mathfuncs -FL zsh/mathfunc - mathfuncs="("${(j.|.)${mathfuncs##f:}}")" + zmodload -P _mathfuncs -FL zsh/mathfunc + _mathfuncs="("${(j.|.)${_mathfuncs##f:}}")" fi -local -A userfuncs -for line in ${(f)"$(functions -M)"}; do - match=(${=line}) +local -A _userfuncs +for _line in ${(f)"$(functions -M)"}; do + match=(${=_line}) # get minimum number of arguments - userfuncs[${match[3]}]=${match[4]} + _userfuncs[${match[3]}]=${match[4]} done -line= +_line= autoload -Uz zmathfuncdef if (( ! ${+ZCALCPROMPT} )); then @@ -155,118 +157,118 @@ fi # Process command line while [[ -n $1 && $1 = -(|[#-]*|f|e|r(<->|)) ]]; do - optlist=${1[2,-1]} + _optlist=${1[2,-1]} shift - [[ $optlist = (|-) ]] && break - while [[ -n $optlist ]]; do - opt=${optlist[1]} - optlist=${optlist[2,-1]} - case $opt in + [[ $_optlist = (|-) ]] && break + while [[ -n $_optlist ]]; do + _opt=${_optlist[1]} + _optlist=${_optlist[2,-1]} + case $_opt in ('#') # Default base - if [[ -n $optlist ]]; then - arg=$optlist - optlist= + if [[ -n $_optlist ]]; then + _arg=$_optlist + _optlist= elif [[ -n $1 ]]; then - arg=$1 + _arg=$1 shift else print -- "-# requires an argument" >&2 return 1 fi - if [[ $arg != (|\#)[[:digit:]]## ]]; then + if [[ $_arg != (|\#)[[:digit:]]## ]]; then print -- "-# requires a decimal number as an argument" >&2 return 1 fi - defbase="[#${arg}]" + _defbase="[#${_arg}]" ;; (f) # Force floating point operation setopt forcefloat ;; (e) # Arguments are expressions - (( expression_mode = 1 )); + (( _expression_mode = 1 )); ;; (r) # RPN mode. - (( rpn_mode = 1 )) + (( _rpn_mode = 1 )) ZCALC_ACTIVE=rpn - if [[ $optlist = (#b)(<->)* ]]; then - (( show_stack = ${match[1]} )) - optlist=${optlist[${#match[1]}+1,-2]} + if [[ $_optlist = (#b)(<->)* ]]; then + (( _show_stack = ${match[1]} )) + _optlist=${_optlist[${#match[1]}+1,-2]} fi ;; esac done done -if (( expression_mode )); then - expressions=("$@") +if (( _expression_mode )); then + _expressions=("$@") argv=() fi -for (( num = 1; num <= $#; num++ )); do +for (( _num = 1; _num <= $#; _num++ )); do # Make sure all arguments have been evaluated. # The `$' before the second argv forces string rather than numeric # substitution. - (( argv[$num] = $argv[$num] )) - print "$num> $argv[$num]" + (( argv[$_num] = $argv[$_num] )) + print "$_num> $argv[$_num]" done -psvar[1]=$num -local prev_line cont_prompt -while (( expression_mode )) || - vared -cehp "${cont_prompt}${ZCALCPROMPT}" line; do - if (( expression_mode )); then - (( ${#expressions} )) || break - line=$expressions[1] - shift expressions +psvar[1]=$_num +local _prev_line _cont_prompt +while (( _expression_mode )) || + vared -cehp "${_cont_prompt}${ZCALCPROMPT}" _line; do + if (( _expression_mode )); then + (( ${#_expressions} )) || break + _line=$_expressions[1] + shift _expressions fi - if [[ $line = (|*[^\\])('\\')#'\' ]]; then - prev_line+=$line[1,-2] - cont_prompt="..." - line= + if [[ $_line = (|*[^\\])('\\')#'\' ]]; then + _prev_line+=$_line[1,-2] + _cont_prompt="..." + _line= continue fi - line="$prev_line$line" - prev_line= - cont_prompt= + _line="$_prev_line$_line" + _prev_line= + _cont_prompt= # Test whether there are as many open as close - # parentheses in the line so far. - if [[ ${#line//[^\(]} -gt ${#line//[^\)]} ]]; then - prev_line+=$line - cont_prompt="..." - line= + # parentheses in the _line so far. + if [[ ${#_line//[^\(]} -gt ${#_line//[^\)]} ]]; then + _prev_line+=$_line + _cont_prompt="..." + _line= continue fi - [[ -z $line ]] && break + [[ -z $_line ]] && break # special cases # Set default base if `[#16]' or `[##16]' etc. on its own. # Unset it if `[#]' or `[##]'. - if [[ $line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then + if [[ $_line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then if [[ -z $match[6] ]]; then if [[ -z $match[3] ]]; then - defbase= + _defbase= else - defbase=$match[1] + _defbase=$match[1] fi - print -s -- $line - print -- $(( ${defbase} ans )) - line= + print -s -- $_line + print -- $(( ${_defbase} ans )) + _line= continue else - base=$match[1] + _base=$match[1] fi else - base=$defbase + _base=$_defbase fi - print -s -- $line + print -s -- $_line - line="${${line##[[:blank:]]#}%%[[:blank:]]#}" - case "$line" in + _line="${${_line##[[:blank:]]#}%%[[:blank:]]#}" + case "$_line" in # Escapes begin with a colon (:(\\|)\!*) # shell escape: handle completion's habit of quoting the ! - eval ${line##:(\\|)\![[:blank:]]#} - line= + eval ${_line##:(\\|)\![[:blank:]]#} + _line= continue ;; @@ -276,83 +278,83 @@ while (( expression_mode )) || ;; ((:|)norm) # restore output format to default - outform=1 + _outform=1 ;; ((:|)sci[[:blank:]]#(#b)(<->)(#B)) - outdigits=$match[1] - outform=2 + _outdigits=$match[1] + _outform=2 ;; ((:|)fix[[:blank:]]#(#b)(<->)(#B)) - outdigits=$match[1] - outform=3 + _outdigits=$match[1] + _outform=3 ;; ((:|)eng[[:blank:]]#(#b)(<->)(#B)) - outdigits=$match[1] - outform=4 + _outdigits=$match[1] + _outform=4 ;; (:raw) - outform=5 + _outform=5 ;; ((:|)local([[:blank:]]##*|)) - eval $line - line= + eval $_line + _line= continue ;; ((function|:f(unc(tion|)|))[[:blank:]]##(#b)([^[:blank:]]##)(|[[:blank:]]##([^[:blank:]]*))) zmathfuncdef $match[1] $match[3] - userfuncs[$match[1]]=${$(functions -Mm $match[1])[4]} - line= + _userfuncs[$match[1]]=${$(functions -Mm $match[1])[4]} + _line= continue ;; (:*) print "Unrecognised escape" - line= + _line= continue ;; (\$[[:IDENT:]]##) # Display only, no calculation - line=${line##\$} - print -r -- ${(P)line} - line= + _line=${_line##\$} + print -r -- ${(P)_line} + _line= continue ;; (*) - line=${${line##[[:blank:]]##}%%[[:blank:]]##} - if [[ rpn_mode -ne 0 && $line != '' ]]; then - push=1 - matched=1 - case $line in + _line=${${_line##[[:blank:]]##}%%[[:blank:]]##} + 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= + print -r -- "${_line}: not enough values on stack" >&2 + _line= continue fi - case $line in + case $_line in (=) ans=${stack[1]} ;; (pop|\<) - push=0 + _push=0 shift stack ;; (\<[[:IDENT:]]##) - (( ${line##\<} = ${stack[1]} )) - push=0 + (( ${_line##\<} = ${stack[1]} )) + _push=0 shift stack ;; (*) print "BUG in special RPN functions" >&2 - line= + _line= continue ;; esac @@ -361,98 +363,101 @@ while (( expression_mode )) || (+|-|\^|\||\&|\*|/|\*\*|\>\>|\<\&2 - line= + print -r -- "${_line}: not enough values on stack" >&2 + _line= continue fi - eval "(( ans = \${stack[2]} $line \${stack[1]} ))" + eval "(( ans = \${stack[2]} $_line \${stack[1]} ))" shift 2 stack ;; (ldexp|jn|yn|scalb|xy) # Functions with two arguments if (( ${#stack} < 2 )); then - print -r -- "${line}: not enough values on stack" >&2 - line= + print -r -- "${_line}: not enough values on stack" >&2 + _line= continue fi - if [[ $line = xy ]]; then - tmp=${stack[1]} + if [[ $_line = xy ]]; then + _tmp=${stack[1]} stack[1]=${stack[2]} - stack[2]=$tmp - push=0 + stack[2]=$_tmp + _push=0 else - eval "(( ans = ${line}(\${stack[2]},\${stack[1]}) ))" + eval "(( ans = ${_line}(\${stack[2]},\${stack[1]}) ))" shift 2 stack fi ;; - (${~mathfuncs}) + (${~_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= + print -r -- "${_line}: not enough values on stack" >&2 + _line= continue fi - eval "(( ans = ${line}(\${stack[1]}) ))" + eval "(( ans = ${_line}(\${stack[1]}) ))" shift stack ;; - (${(kj.|.)~userfuncs}) + (${(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= + _n=${_userfuncs[$_line]} + if (( ${#stack} < n_ )); then + print -r -- "${_line}: not enough values ($_n) on stack" >&2 + _line= continue fi - line+="(" + _line+="(" # least recent elements on stack are earlier arguments - for (( i = n; i > 0; i-- )); do - line+=${stack[i]} - (( i > 1 )) && line+="," + for (( _i = _n; _i > 0; _i-- )); do + _line+=${stack[_i]} + (( _i > 1 )) && _line+="," done - line+=")" - shift $n stack - eval "(( ans = $line ))" + _line+=")" + shift $_n stack + eval "(( ans = $_line ))" ;; (*) # Treat as expression evaluating to new value to go on stack. - matched=0 + _matched=0 ;; esac else - matched=0 + _matched=0 fi - if (( ! matched )); then + 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 + if ! eval "ans=\$(( $_line ))"; then + _line= + continue + fi + # on error $ans is not set; let user re-edit _line [[ -n $ans ]] || continue fi - argv[num++]=$ans - psvar[1]=$num - (( push )) && stack=($ans $stack) + argv[_num++]=$ans + psvar[1]=$_num + (( _push )) && stack=($ans $stack) ;; esac - 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]} + 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 zcalc_show_value $ans fi - line= + _line= done return 0 -- cgit v1.2.3 From a7d5d239e6ab729515083a88cfaf955e078c1685 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 4 Jul 2016 12:08:14 +0100 Subject: 38783: zcalc tweaks for RPN mode. Make it more straightforward to exchange variables with stack. --- ChangeLog | 5 +++++ Doc/Zsh/contrib.yo | 16 ++++++++++++---- Functions/Misc/zcalc | 26 ++++++++++++++++++-------- 3 files changed, 35 insertions(+), 12 deletions(-) (limited to 'Functions/Misc/zcalc') diff --git a/ChangeLog b/ChangeLog index 831ea4e69..952b240c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2016-07-04 Peter Stephenson + + * 38783: Doc/Zsh/contrib.yo, Functions/Misc/zcalc: tweaks for + variable and stack interation in RPN mode. + 2016-06-29 Oliver Kiddle * 38770: Src/Zle/zle_keymap.c, Src/Zle/zle_vi.c, Doc/Zsh/zle.yo, diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index f1208e843..d4a453849 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -3829,16 +3829,24 @@ 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. +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 variable with that name. The variable is +local to the tt(zcalc) function. ) 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. +causes the value of the variable with that name to be pushed +onto the stack. var(ident) may be an integer, in which +case the previous result with that number (as shown before +the tt(>) in th standard standard tt(zcalc) prompt) is put on the stack. ) item(Exchange: tt(xy))( The pseudo-function tt(xy) causes the most recent two elements of -the stack to be exchanged. +the stack to be exchanged. `tt(<>)' has the same effect. ) enditem() diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc index 86b1e4a5b..480373345 100644 --- a/Functions/Misc/zcalc +++ b/Functions/Misc/zcalc @@ -94,7 +94,7 @@ # sequentially just as if read automatically. emulate -L zsh -setopt extendedglob +setopt extendedglob typesetsilent zcalc_show_value() { if [[ -n $_base ]]; then @@ -301,7 +301,7 @@ while (( _expression_mode )) || ;; ((:|)local([[:blank:]]##*|)) - eval $_line + eval ${_line##:} _line= continue ;; @@ -333,7 +333,11 @@ while (( _expression_mode )) || _push=1 _matched=1 case $_line in - (\=|pop|\<[[:IDENT:]]#) + (\<[[:IDENT:]]##) + ans=${(P)${_line##\<}} + ;; + + (\=|pop|\>[[:IDENT:]]#) if (( ${#stack} < 1 )); then print -r -- "${_line}: not enough values on stack" >&2 _line= @@ -343,12 +347,18 @@ while (( _expression_mode )) || (=) ans=${stack[1]} ;; - (pop|\<) + (pop|\>) _push=0 shift stack ;; - (\<[[:IDENT:]]##) - (( ${_line##\<} = ${stack[1]} )) + (\>[[:IDENT:]]##) + if [[ ${_line##\>} = (_*|stack|ans|PI|E) ]]; then + print "${_line##\>}: reserved variable" >&2 + _line= + continue + fi + local ${_line##\>} + (( ${_line##\>} = ${stack[1]} )) _push=0 shift stack ;; @@ -371,14 +381,14 @@ while (( _expression_mode )) || shift 2 stack ;; - (ldexp|jn|yn|scalb|xy) + (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 - if [[ $_line = xy ]]; then + if [[ $_line = (xy|\<\>) ]]; then _tmp=${stack[1]} stack[1]=${stack[2]} stack[2]=$_tmp -- cgit v1.2.3