rpn(1) #36
Loading…
x
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 NAMEnpc \(en reverse polish notation evaluationnpc?
@ -0,0 +11,4 @@.SH SYNOPSISrpn [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 INPUTIf rpn is passed arguments, it interprets those arguments as an expression tobe 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 CAVEATSDue to the complexity of integer storage in memory, rpn is only capable ofparsing 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 asprecise as slightly less than the machine epsilon of the hardware on which rpnis 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 inputfor its calculations. Pair the clunkiness of piping expressions into it and itsuse 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 thatmath was the job of a specific tool and not the shell itself. Thus, rpn wasborn.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
#defines as they replace a given symbol with another symbol, though unlike#defines 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)ncan 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 DESCRIPTIONRpn parses and and evaluates reverse polish notation expressions either from thestandard 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 thestandard output. Any further specified numbers will be placed on the stackfollowing 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 INPUTIf 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-pointarithmetic has rounding errors. This is somewhat curbed by using thesecond-highest float that can be represented in line with this standard to roundnumbers 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 RATIONALEAn infix notation calculation utility, bc(1p), is included in the POSIXstandard, but it doesn’t accept expressions as arguments; in scripts, anyI 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