Hacker News new | ask | show | jobs
by cdoxsey 2678 days ago
This algorithm is great.

I was working on a new metric alert evaluation system and built an expression parser for things like:

    system.disk.free{*} / 1024 by {host}
The naive approach to parsing will result in the wrong order of operations, since they'll just be done in the order they appear:

    x + y * z   =>   (x + y) * z
Rather than:

    x + (y * z)
As it should be. The shunting yard algorithm will rearrange the expressions.

After implementing it I noticed my results were different than the reference system... and that's when I discovered we did arithmetic wrong in the main app and had for years.

So I had to hard code a toggle for whether to do math properly :(. It's a sort of Hippocratic oath when it comes to these things... first do no harm, and even if it was wrong, people were relying on the existing functionality, and changing it would likely result in sudden alerts for folks.

In the end we did fix it in the main app, but you always feel kind of dirty writing code like that.

2 comments

Just the other day I was thinking about how all this infix business is needlessly complicated and leads to subtle bugs and hard to understand code. Like every time I encounter an uncommon operator in some language I have to lookup its precedence. So much time wasted trying to satisfy this silly familiarity with math notation. Only imagine how much easier things could be if all infix operators were, for example, left associative and had the same precedence. No more parsing bugs, no implicit orders and behaviors to remember, consistent order, even more natural and familiar than the math notation allowing to focus on things that matter and forget about dealing with precedence and associativity.
If you're going to put all operators at the same level of precedence, you've accepted lots of parentheses anyway; so why not just go all the way and require parentheses everywhere, meaning that no associativity need be specified either?
I've always thought it would be cool to have a language where functions were tagged as distributive, associative, symmetric/commutative, and monotonic and the optimizer (and editor!) used these properties to determine optimizations or simplifications. Note that this would be for all expressions, not just arithmetical ones.

I vaguely recall hearing about some fancy C++ template based techniques for accomplishing this, actually, but haven't looked into it.

Swift has operator precedence levels, and you are free to use them for your own operators: https://developer.apple.com/documentation/swift/swift_standa...
Mathematica/the Wolfram language does that. e.g the Flat attribute denotes associativity, etc. https://reference.wolfram.com/language/ref/Flat.html
I also wonder that. However how much "dislike" will cause to force to use parents for math code? For me, I could live with that, but I'm not a math-heavy coder...

How much other stuff, apart of math, could be impacted?

because essentially, Shunting yard algorithm is just a LR(0) parser, it awaits sufficient information first in a post-order manner (shifting), then each time you push to the final queue if a production is found (reducing)