Mike Sanders
2023-11-06 02:26:38 UTC
Ping Janis: Question... Are you interested in rewriting this
as a Gawk only implementation? Would be great for switch/case
statements IMO. If so, I'll add your version to the file at
my website.
Folks, assuming Janis says yes, lets work off that version
& use it as a baseline.
At any rate, a work in progress...
# tags: rpn, calc, numbers, awk, code
#
# awk reverse polish notation parser
# Michael Sanders 2023
# https://busybox.neocities.org/notes/rpn.txt
#
# operands...
#
# large numbers? this really depends on your awk
# decimal fractions? yes, via a hopfully not too
# clever regex that expects decimal points to be
# surrounded by digits eg...
#
# valid 0.5, invalid .5
# valid 5.0, invalid 5.
#
# operators...
#
# + addition
# - subtraction
# * multiplication
# / division
# % modulus
# ^ exponentiation (see footnotes)
#
# *always* surround input with 'quotes' as some RPN
# operators can be misconstrued as meta-characters
# by your shell, example...
#
# echo '0.089 2.5 + 2 * 3 ^' | awk -f rpn.txt
#
# arbitrary precision using printf format specifier...
#
# %0.0f 1
# %0.2f 1.00 (default)
# %0.9f 1.000000000
#
# further reading...
#
# https://en.wikipedia.org/wiki/Reverse_Polish_notation
{ RPN($0) }
function RPN(input, x, y, z, stack) {
split(input, stack, /[[:space:]]+/)
z = 0
for (x = 1; x in stack; ++x) {
y = stack[x]
if (y ~ /^[0-9]+(\.[0-9]+)?$/) stack[++z] = y
else {
if (z < 2) { print "error: insufficient operands"; return }
if (y == "+") stack[z-1] += stack[z]
else if (y == "-") stack[z-1] -= stack[z]
else if (y == "*") stack[z-1] *= stack[z]
else if (y == "^") stack[z-1] ^= stack[z] # see footnotes
else if (y == "/") {
if (stack[z] == 0) { print "error: division by zero"; return }
stack[z-1] /= stack[z]
} else if (y == "%") {
if (stack[z] == 0) { print "error: modulo by zero"; return }
stack[z-1] %= stack[z]
} else { print "error: invalid operator " y; return }
--z
}
}
if (z != 1) { print "error: invalid RPN expression"; return }
printf "%0.2f\n", stack[z]
}
# footnotes: exponentiation operators for differing awks...
#
# awk 'BEGIN {print 2 ^ 3}' should print 8 if using ^
# stack[z-1] ^= stack[z] works on some awks using ^
# stack[z-1] = stack[z-1] ^ stack[z] always works if using ^
#
# awk 'BEGIN {print 2 ** 3}' should print 8 if using **
# stack[z-1] **= stack[z] works on some awks using **
# stack[z-1] = stack[z-1] ** stack[z] always works if using **
# eof
as a Gawk only implementation? Would be great for switch/case
statements IMO. If so, I'll add your version to the file at
my website.
Folks, assuming Janis says yes, lets work off that version
& use it as a baseline.
At any rate, a work in progress...
# tags: rpn, calc, numbers, awk, code
#
# awk reverse polish notation parser
# Michael Sanders 2023
# https://busybox.neocities.org/notes/rpn.txt
#
# operands...
#
# large numbers? this really depends on your awk
# decimal fractions? yes, via a hopfully not too
# clever regex that expects decimal points to be
# surrounded by digits eg...
#
# valid 0.5, invalid .5
# valid 5.0, invalid 5.
#
# operators...
#
# + addition
# - subtraction
# * multiplication
# / division
# % modulus
# ^ exponentiation (see footnotes)
#
# *always* surround input with 'quotes' as some RPN
# operators can be misconstrued as meta-characters
# by your shell, example...
#
# echo '0.089 2.5 + 2 * 3 ^' | awk -f rpn.txt
#
# arbitrary precision using printf format specifier...
#
# %0.0f 1
# %0.2f 1.00 (default)
# %0.9f 1.000000000
#
# further reading...
#
# https://en.wikipedia.org/wiki/Reverse_Polish_notation
{ RPN($0) }
function RPN(input, x, y, z, stack) {
split(input, stack, /[[:space:]]+/)
z = 0
for (x = 1; x in stack; ++x) {
y = stack[x]
if (y ~ /^[0-9]+(\.[0-9]+)?$/) stack[++z] = y
else {
if (z < 2) { print "error: insufficient operands"; return }
if (y == "+") stack[z-1] += stack[z]
else if (y == "-") stack[z-1] -= stack[z]
else if (y == "*") stack[z-1] *= stack[z]
else if (y == "^") stack[z-1] ^= stack[z] # see footnotes
else if (y == "/") {
if (stack[z] == 0) { print "error: division by zero"; return }
stack[z-1] /= stack[z]
} else if (y == "%") {
if (stack[z] == 0) { print "error: modulo by zero"; return }
stack[z-1] %= stack[z]
} else { print "error: invalid operator " y; return }
--z
}
}
if (z != 1) { print "error: invalid RPN expression"; return }
printf "%0.2f\n", stack[z]
}
# footnotes: exponentiation operators for differing awks...
#
# awk 'BEGIN {print 2 ^ 3}' should print 8 if using ^
# stack[z-1] ^= stack[z] works on some awks using ^
# stack[z-1] = stack[z-1] ^ stack[z] always works if using ^
#
# awk 'BEGIN {print 2 ** 3}' should print 8 if using **
# stack[z-1] **= stack[z] works on some awks using **
# stack[z-1] = stack[z-1] ** stack[z] always works if using **
# eof
--
:wq
Mike Sanders
:wq
Mike Sanders