Parser: Add error when operator precedence is undefined #25275
+269
−139
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Implements a version of the operator precedence @thejoshwolfe described in #114.
All operators are broken into operator classes which can feature a "minor" version for intraclass relations.
Operator Classes
Arithmetic-0
consists of {*
*%
*|
/
||
}Arithmetic-1
consists of {**
%
} and is not chainable (throwing a new error)Arithmetic-2
consists of {+
-
+%
-%
+|
-|
++
}Bitwise-0
consists of {<<
<<|
>>
}Bitwise-1
consists of {&
}Bitwise-2
consists of {^
}Bitwise-3
consists of {|
}Coercion
consists of {orelse
catch
}Comparison
consists of {==
!=
<
<=
>=
>
} and is not chainable (throwing the old error)Logical-0
consists of {and
}Logical-1
consists of {or
}Operator Class Major Relationships
arithmetic
>coercion
bitwise
>coercion
coercion
>comparison
>logical
Operator Class Minor Relationships
a0
==a1
>a2
b0
>b1
>b3
l0
>l1
Major relationships are defined in a table in Parse.Zig, while minor is defined in the comparison function.
The parser changes are limited to replacing precedence numbers with classes and delegating comparisons to the new comparison function which is configurable at compile-time. If two operators do not have a defined precedence relationship, the parser raises an error instructing the user to add disambiguating parentheses.
Rationale for the defined relationships
Arithmetic precedence is generally well-defined. The
%
and**
operators are not chainable, as constructs such asa % b % c
anda ** b ** c
result in ambiguous, confusing, or unintuitive code that should be reconsidered by the programmer.Bitwise
&
and|
are analogous to*
and+
, in all the languages I surveyed (C, (old Zig), Python, Java, Odin), bit shifts have higher precedence than other bitwise operators. Additionally, bit shifts precede|
, as constructs likea << b | c
are common in bit packing and is often expected behavior.^
has no analogous arithmetic operation (except maybe+%
), so for this reason its not defined in relation to those.Symbolic operators coming before keyword operators is, in my opinion, intuitive, however I've lofted coercion operators above comparisons as it would never trigger below them and we do often see syntax like
dict.get(item) orelse .default != .default
Finally,
and
andor
are treated analogously to*
and+
, and are consistently defined across languages as having lower precedence than comparisons.The most common source of code that needed to be updated was mixing
+
with<<
and mixing+
with&
; the relationship between arithmetic and bitwise operators is left undefined, since language conventions are inconsistent and programmer expectations may vary.Fixes #114