zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: zcalc RPN part 2
@ 2016-06-21 15:28 Peter Stephenson
  2016-06-21 16:09 ` Peter Stephenson
  0 siblings, 1 reply; 2+ messages in thread
From: Peter Stephenson @ 2016-06-21 15:28 UTC (permalink / raw)
  To: Zsh Hackers' List

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


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: PATCH: zcalc RPN part 2
  2016-06-21 15:28 PATCH: zcalc RPN part 2 Peter Stephenson
@ 2016-06-21 16:09 ` Peter Stephenson
  0 siblings, 0 replies; 2+ messages in thread
From: Peter Stephenson @ 2016-06-21 16:09 UTC (permalink / raw)
  To: Zsh Hackers' List

On Tue, 21 Jun 2016 16:28:28 +0100
Peter Stephenson <p.stephenson@samsung.com> wrote:
> 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.

Here is documentation for variables after that change.

pws

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index c875c95..53ae96d 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.
 )


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2016-06-21 16:09 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-21 15:28 PATCH: zcalc RPN part 2 Peter Stephenson
2016-06-21 16:09 ` Peter Stephenson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).