rpn(1) #36
No reviewers
Labels
No Label
bug
duplicate
enhancement
help wanted
invalid
joke
question
wontfix
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: bonsai/harakit#36
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "rpn"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #21
I'd like to see the bitwise operators and floor division from #21 included but all in all i think this is a good implementation and better than I could have done (or even intended to do).
@ -58,3 +58,3 @@
libgetopt: src/getopt-rs/lib.rs
$(RUSTC) $(RUSTCFLAGS) --crate-type=lib --crate-name=getopt \
$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
Can this change be made independent of rpn(1) or is it necessary for rpn(1) in particular?
No reason to walk back the commit now that it’s been made
@ -0,0 +69,4 @@
}
#[derive(Debug, Clone)]
struct EvaluationError {
Can we emit argv[0] before printing errors?
If the program does not do that on your machine, you are encountering a bug that I cannot reproduce.
I haven't run it on my machine, I just didn't see that it did that.
@ -0,0 +85,4 @@
// str_to_calc_type converts a string to an optional `CalcType`.
fn str_to_calc_type(string: &str) -> Option<CalcType> {
let as_int = string.parse::<f64>();
Will this work for non-decimal integers, e.g. 0x10 (16), 0o10 (8), 0b1 (2)?
Implementing this would be at the cost of storing numbers as signed 32-bit integers before converting them to 64-bit floats, which would lessen the maximum size of the integers we can take.
@ -0,0 +106,4 @@
}
}
fn eval(
I would prefer evaluate() only because Python, shell, and Lisp have beaten terror at the sight of the abbreviation into me.
@ -0,0 +132,4 @@
};
match x {
Add | Divide | Multiply | Power | Subtract | Modulo => {
Is this just every CalcType besides Val? Is there a better way to write that, e.g. match except Val?
(this isn't a rhetorical question i don't know rust)
@ -0,0 +156,4 @@
Multiply => &stack.push_back(y * x),
Divide => &stack.push_back(y / x),
Power => &stack.push_back(x.powf(*y)),
Modulo => &stack.push_back(y % x),
I chipped in my thought on making sure rpn(1) functions like HP and Elektronika calculators when operating and this implementation satisfies me.
@ -0,0 +183,4 @@
let argv = args().collect::<Vec<String>>();
let mut stack = VecDeque::new();
let mut buf = String::new();
let precision = (-f64::EPSILON.log10() * PRECISION_MOD).ceil() as usize;
What's happening here?
Setting the float precision for rounding errors based on the machine epsilon
I’m currently writing
rpn(1)
and will be writingrpn(7)
for a comprehensive guide to reverse polish notation as soon as I get around to it.good stuff
@ -0,0 +7,4 @@
.SH NAME
npc \(en reverse polish notation evaluation
npc?
@ -0,0 +11,4 @@
.SH SYNOPSIS
rpn [integers...] [operations...]
Integers and operations can be mixed. I would classify an integer as an operation, too - the push operation.
@ -0,0 +13,4 @@
rpn [integers...] [operations...]
.SH DESCRIPTION
"Rpn evaluates a given reverse polish notation expression according to the conventions defined in rpn(7), printing the last item on its stack on completion."
@ -0,0 +22,4 @@
.SH STANDARD INPUT
If rpn is passed arguments, it interprets those arguments as an expression to
be evaluated. Otherwise, it reads characters from standard input to add to the
"Otherwise, it reads a newline-delimited series of numbers and operations from standard input."
They aren’t necessarily newline-delimited.
whitespace-delimited
@ -0,0 +33,4 @@
.SH CAVEATS
Due to the complexity of integer storage in memory, rpn is only capable of
parsing decimal integers.
I would classify this as a bug, not a caveat, and refrain from mentioning implementation details in the man page.
It looks like dc(1) historically also had this issue. I'd really like to not have it though and I might try to fix this myself.
@ -0,0 +37,4 @@
Additionally, due to the laws of physics, floating-point math can only be as
precise as slightly less than the machine epsilon of the hardware on which rpn
is running.
I don't think this caveat is needed here, maybe there should be a float(7) that explains IEEE-754 and its caveats.
@ -0,0 +43,4 @@
POSIX has its own calculator in the form of bc(1p), which uses standard input
for its calculations. Pair the clunkiness of piping expressions into it and its
use of standard notation, it was clear what rpn should be.
This reads weirdly. I would say:
"An infix notation calculation utility, bc(1p), is specified in POSIX, but doesn't accept expressions in arguments, requiring printf(1p) to be piped into bc(1p) in order to be used non-interactively. A dc(1) pre-dates the standardized bc(1p), the latter originally being a preprocessor for the former, and was included in UNIX v2 onward. While it implements reverse polish notation it still suffers from being unable to accept an expression as an argument."
See Early UNIX history (which says dc(1) was implemented by 1970) and the UNIX v2 source tree.
@ -0,0 +47,4 @@
There are no mathematics in the qi(1) shell because it was decided early on that
math was the job of a specific tool and not the shell itself. Thus, rpn was
born.
This probably belongs in a qi(1) man page, not here.
@ -0,0 +68,4 @@
Modulo,
Val(f64),
Empty,
}
If this were C I would put the sentry value first so validity is just a measure of whether or not the enum is 0. I don't know if Rust works that way but it's a thought. It may also make more sense conceptually if Empty and Val are the zeroth and first enumerations respectively.
What does it mean for an enum to be zero?
In C - I can't speak as to how Rust does it - enums are basically
#define
s as they replace a given symbol with another symbol, though unlike#define
s the symbol remains in the code rather than being a text replacement (which at some point probably mattered for debugging but I don't notice the difference). The enum being zero means a given value (e.g.Empty
) evaluates to0
.I don’t think that’s how it works in Rust—also, if I recall correctly, as far as I’ve seen in other people’s code, the error variant is usually last, so I feel there is some convention here. I will look for some examples of this to confirm.
With further testing, I’ve realized the
eval()
function needs to be rewritten practically from scratch to prevent edge cases in the parsing.@ -0,0 +77,4 @@
"-" | "−" => Subtract,
"*" | "×" => Multiply,
"/" | "÷" => Divide,
"^" => Power,
I would use
**
for exponentiation as^
is conventionally used for bitwise xor.I think
**
should be allowed, it's a common name for the operation. It doesn't have to be the only name but it should be an option.@ -0,0 +148,4 @@
Val(v) => stack.push_back(v),
Invalid => {
return Err(EvaluationError {
message: format!("{}: Invalid token", n)
n
can only ever beInvalid
.Next goal: I would like to include error codes in the EvaluationError struct.
WIP: rpn(1)to rpn(1)I've suggested minor fixes but the code looks alright.
@ -0,0 +18,4 @@
.SH DESCRIPTION
Rpn parses and and evaluates reverse polish notation expressions either from the
standard input or by parsing its arguments. See the STANDARD INPUT section.
"either from given arguments, or, in their absence, standard input" - right now you have "parse" twice in there which feels weird.
@ -0,0 +22,4 @@
Upon evaluation, rpn will print the resulting number on the stack to the
standard output. Any further specified numbers will be placed on the stack
following the last outputted number.
I believe the past tense of "output" is "output" - also, I'm having a hard time understanding what you're trying to convey here - shouldn't rpn print the last number on the stack? It sounds like it prints the last result of any algebraic operations but ignores further given numbers.
According to Merriam-Webster [1], the verb outputted is the past tense form of output. I want to change this paragraph regardless because it is a bit clunky.
[1] https://www.merriam-webster.com/dictionary/outputted
@ -0,0 +28,4 @@
.SH STANDARD INPUT
If rpn is passed arguments, it interprets them as an expression to be evaluated.
"If arguments are passed to rpn" reads cleaner to me but I can't put my finger on why.
@ -0,0 +43,4 @@
with the IEEE Standard for Floating Point Arithmetic (IEEE 754), floating-point
arithmetic has rounding errors. This is somewhat curbed by using the
second-highest float that can be represented in line with this standard to round
numbers to before outputting.
It's less in accord with IEEE 754 and more that we are applying IEEE 754 (or, more specifically, Rust is - and even then, it's the processor itself I think that is doing the floating point operations). I would say "Due to IEEE 754 (the IEEE Standard for Floating Point Arithmetic) limitations, rounding errors may occur. And not "in line" with the standard but simply "that can be represented".
What's the second-highest float? This man page leaves me unclear on how much I should lean on rpn for fractional values.
This does not imply any work on our part. The floats are represented in accordance with IEEE standards in Rust, which is implicit in the sentence. It is equivalent to saying:
@ -0,0 +48,4 @@
.SH RATIONALE
An infix notation calculation utility, bc(1p), is included in the POSIX
standard, but it doesn’t accept expressions as arguments; in scripts, any
I don't like the use of the Unicode apostrophe here because it could mess with literal text lookups. Nobody likes a
No occurrences of "doesn't"
because of fancy rune voodoo.@ -0,0 +99,4 @@
Multiply => "multiplication",
Divide => "division",
Power => "exponentiation",
Floor => "floor divion",
division
Pull request closed