Are there benefits to number literal tokens including negative numbers? Or is the string
-1234 being parsed as unary negation of positive number literal
1234 more beneficial?
In C, negative literals are processed as unary negation on a literal. So,
INT_MIN must be defined as
-2147483647 - 1 rather than
In Swift, negative literals can be a single token. However, this means
-0 as Double and
-0.0 are different.
-(0 as Double), however, are the same.
However, Swift isn't even consistent about this. When method calls or postfix operators are involved, they bind more tightly than the unary minus, so
-0.foo() is parsed as
-(0.foo()). As I mentioned in a comment, this means that in some cases,
(-(0)).foo() are all different.
Do both (avoid abiguity or parenthesis)
The overloading of
- for negative literals and negation causes ambiguity:W
In mathematics and most programming languages, the rules for the order of operations mean that −52 is equal to −25: Exponentiation binds more strongly than the unary minus, which binds more strongly than multiplication or division. However, in some programming languages (Microsoft Excel in particular), unary operators bind strongest, so in those cases
−5^2is 25, but
To avoid this (and to allow adjacent numerical tokens not causing subtraction) a language can use a separate character for negative literals. APL uses
¯ while J uses
The advantage of “unary negated positive literals” means avoiding a special case in your parser. You can treat the
-1234 the same way that you treat the
-(x * y + z). It is for this reason that Jack Crenshaw's Let's Build a Compiler chose that approach (as well as rewriting unary
-x to binary
It does theoretically make the generated machine code slightly less efficient, but a simple constant folding optimization will turn the expression into the desired negative constant.
This is probably the big one. If you have a negation operator rather than negative literals, you can replace
a is an integer variable) everywhere, and nothing else about the program will change.
This isn't true if
-5 is a token.
Interaction with other syntax
To quote from the Haskell 2010 report, section 10.6:
The handling of the prefix negation operator,
-, complicates matters only slightly. Recall that prefix negation has the same fixity as infix negation: left-associative with precedence 6. The operator to the left of
-, if there is one, must have precedence lower than 6 for the expression to be legal. The negation operator itself may left-associate with operators of the same fixity (e.g.
+). So for example
-a + bis legal and resolves as
(-a) + b, but
a + -bis illegal.
The negation operator also interacts with function application in ways that may be non-obvious to those coming from other languages;
f -1 doesn't mean
f (-1), it means
f - 1.
To complicate things even further, Haskell doesn't have a unary plus operator, so
f (-1) means negative one passed to the function
f (+1) means the operator section
(\x -> x + 1) passed to
This is not unique to Haskell, but it's made more obvious because of the (otherwise extremely convenient) function call and operator section syntax. Some variants of Prolog have related issues. Now that I think of it, I doubt there are two Prolog implementations which parse the same way.
For comparison, C has 13 levels of operator precedence and many operators which can be both prefix and infix. Most C programmers never seem to find themselves needing to put parentheses around a negative integer, but the same can't be said of the prefix
- is a binary operator in your language, then you probably shouldn't make
-1 a single token. If you do, then
x-1 will be lexed as two tokens ─ an identifier
x followed by a literal
-1 ─ and then you can't parse it as a binary operator expression like
x - 1, unless your parser has a weird special case for treating negative number literals like postfix operators.
Yes, you also need a special case if you want to reject out-of-bounds integer literals as syntax errors while accepting
INT_MIN, whose absolute value is out-of-bounds. But that's a considerably easier special case. For example, here's how the Java Language Specification handles it:
The largest decimal literal of type
All decimal literals from
2147483647may appear anywhere an int literal may appear. The decimal literal
2147483648may appear only as the operand of the unary minus operator
It is a compile-time error if the decimal literal
2147483648appears anywhere other than as the operand of the unary minus operator; or if a decimal literal of type
intis larger than
There is a similar rule for decimal literals of type