So, if you've ever tried using parsers (whether to make your own language, or make a crazy-good calculator), you've probably been at least partially betrayed by unary operators. Stuff like -x, !flag, or counter++ are just hell for a normal-everyday parser. Don't get me wrong; parsers have made a way, but the different ways of how they do it is just... very complicated. But why? Why are unary operators so finicky and weird?
The Core Problem
Unary operators aren't like their binary counterparts (obviously). they take in one input, and spit out a value. But once you actually look at the tokens the parser gets, you'll start to see the problem. Take, for example, 2 + -x.
The lexer returns something like:
Number(2),
Operator(Add),
Operator(Minus),
Variable("x")
But how do we differentiate -u (unary subtraction) and -b? Let's start simple with Pratt parsing.
Pratt Parsing is a top-down parser where it starts not knowing the full expression, and it slowly iterates through the expression, gaining more context the more it reveals. Stuff like binary operators are easy; it's just binding power and waiting. but how does it parse -u? Well, Pratt Parsing uses led and nud: left-denotation and null-denotation. Basically, it's when an operator has a second number (led) or no second number (nud). When you're trying to use -u, then you would use the nud version of -.
For my parser, it uses disambiguation. Basically, if we had something like 7 + -5, it's going to check the last term. if it's a term starter , like an operator, it's a unary operator. If it's a term itself, it's a binary operator. But, you do need to check for all cases.
Top comments (0)