summaryrefslogtreecommitdiff
path: root/Functions
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2001-07-27 11:34:46 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2001-07-27 11:34:46 +0000
commit08225106263417d4d6124c1ce619b8c8ae447bdb (patch)
tree5e72be1efdc02410fa4c45286eff10c24cadf0ce /Functions
parent47fc2c2662ec417d274b62f65a26edb09725a754 (diff)
downloadzsh-08225106263417d4d6124c1ce619b8c8ae447bdb.tar.gz
zsh-08225106263417d4d6124c1ce619b8c8ae447bdb.zip
15507: new zcalc function
15508: cvs tag didn't complete tags despite it's claim.
Diffstat (limited to 'Functions')
-rw-r--r--Functions/Misc/zcalc142
1 files changed, 142 insertions, 0 deletions
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
new file mode 100644
index 000000000..80a031a8b
--- /dev/null
+++ b/Functions/Misc/zcalc
@@ -0,0 +1,142 @@
+#!/usr/local/bin/zsh -i
+#
+# Zsh calculator. Understands most ordinary arithmetic expressions.
+# Line editing and history are available. A blank line or `q' quits.
+#
+# Runs as a script or a function. If used as a function, the history
+# is remembered for reuse in a later call (and also currently in the
+# shell's own history). There are various problems using this as a
+# script, so a function is recommended.
+#
+# The prompt shows a number for the current line. The corresponding
+# result can be referred to with $<line-no>, e.g.
+# 1> 32 + 10
+# 42
+# 2> $1 ** 2
+# 1764
+# The set of remembered numbers is primed with anything given on the
+# command line. For example,
+# zcalc '2 * 16'
+# 1> 32 # printed by function
+# 2> $1 + 2 # typed by user
+# 34
+# 3>
+# Here, 32 is stored as $1. This works in the obvious way for any
+# number of arguments.
+#
+# If the mathfunc library is available, probably understands most system
+# mathematical functions. The left parenthesis must be adjacent to the
+# end of the function name, to distinguish from shell parameters
+# (translation: to prevent the maintainers from having to write proper
+# lookahead parsing). For example,
+# 1> sqrt(2)
+# 1.4142135623730951
+# is right, but `sqrt (2)' will give you an error.
+#
+# You can do things with parameters like
+# 1> pi = 4.0 * atan(1)
+# too. These go into global parameters, so be careful. You can declare
+# local variables, however:
+# 1> local pi
+# but note this can't appear on the same line as a calculation. Don't
+# use the variables listed in the `local' and `integer' lines below
+# (translation: I can't be bothered to provide a sandbox).
+#
+# Some constants are already available: (case sensitive as always):
+# PI pi, i.e. 3.1415926545897931
+# E e, i.e. 2.7182818284590455
+#
+# You can also change the output base.
+# 1> [#16]
+# 1>
+# Changes the default output to hexadecimal with numbers preceded by `16#'.
+# Note the line isn't remembered.
+# 2> [##16]
+# 2>
+# Change the default output base to hexadecimal with no prefix.
+# 3> [#]
+# Reset the default output base.
+#
+# This is based on the builtin feature that you can change the output base
+# of a given expression. For example,
+# 1> [##16] 32 + 20 / 2
+# 2A
+# 2>
+# prints the result of the calculation in hexadecimal.
+#
+# You can't change the default input base, but the shell allows any small
+# integer as a base:
+# 1> 2#1111
+# 15
+# 2> [##13] 13#6 * 13#9
+# 42
+# and the standard C-like notation with a leading 0x for hexadecimal is
+# also understood. However, leading 0 for octal is not understood --- it's
+# too confusing in a calculator. Use 8#777 etc.
+#
+#
+# To do:
+# - separate zcalc history from shell history using arrays --- or allow
+# zsh to switch internally to and from array-based history.
+# - allow setting number of decimal places for display, scientific notation,
+# etc.
+
+emulate -L zsh
+setopt extendedglob
+
+local line latest base defbase match mbegin mend
+integer num
+
+zmodload -i zsh/mathfunc 2>/dev/null
+
+# Supply some constants.
+float PI E
+(( PI = 4 * atan(1), E = exp(1) ))
+
+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]"
+done
+
+while vared -chp "$num> " line; do
+ [[ -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 [[ -z $match[4] ]]; then
+ if [[ -z $match[3] ]]; then
+ defbase=
+ else
+ defbase=$match[1]
+ fi
+ print -s -- $line
+ line=
+ continue
+ else
+ base=
+ fi
+ else
+ base=$defbase
+ fi
+ # Exit if `q' on its own.
+ [[ $line = [[:blank:]]#q[[:blank:]]# ]] && return 0
+
+ print -s -- $line
+ if [[ $line = [[:blank:]]#local([[:blank:]]##*|) ]]; then
+ eval $line
+ else
+ # 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.
+ eval "latest=\$(( $base $line ))"
+ argv[num++]=$latest
+ print -- $latest
+ fi
+ line=
+done
+
+return 0