//! Precedence tables for Fermented `UnrealScript` operators. //! //! These values don't follow the usual *binding power* convention for //! a Pratt parser, where tighter binding corresponds to a larger number.\ //! Here, the smaller the number, the tighter the binding power.\ //! For this reason, we use the term *precedence rank* instead. //! //! ## Operators sorted by precedence (lowest number = tighter binding) //! //! ### Infix operators //! //! All infix operators in `UnrealScript` are //! [left-associative](https://wiki.beyondunreal.com/Operators). //! //! 12: `**` //! 16: `*`, `/`, `Cross`, `Dot` //! 18: `%` //! 20: `+`, `-` //! 22: `<<`, `>>`, `>>>` //! 24: `<`, `>`, `<=`, `>=`, `==`, `~=`, `ClockwiseFrom` //! 26: `!=` //! 28: `&`, `^`, `|` //! 30: `&&`, `^^` //! 32: `||` //! 34: `*=`, `/=`, `+=`, `-=` //! 40: `$`, `*`, `@` //! 44: `$=`, `*=`, `@=` //! 45: `-=` //! //! Some operator, such as `*`, appear twice with different precedence //! ranks because they were defined with different values for different types //! in separate script source files (as in the Killing Floor sources).\ //! However, `UnrealScript` uses only the first definition it encounters in //! `Object.uc`, which corresponds to the lower value. //! //! ### Prefix operators //! //! `!`, `~`, `+`, `-`, `++`, `--`. //! //! ### Postfix operators //! //! `++`, `--`. use crate::ast::{InfixOperator, infix_operator_info}; use crate::lexer::Token; /// Compact precedence rank used by the Pratt Parser. /// /// A smaller number means tighter binding, and a larger number means looser /// binding. This inverted scale matches how `UnrealScript` tables were recorded. #[must_use] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct PrecedenceRank(u8); impl PrecedenceRank { /// The loosest possible precedence rank. /// /// In this inverted scale (smaller number = tighter binding), /// this is represented by the maximum [`u8`] value. pub const LOOSEST: Self = Self(u8::MAX); /// The tightest possible precedence rank. /// /// In this inverted scale (smaller number = tighter binding), /// this is represented by zero. pub const TIGHTEST: Self = Self(0); /// Returns `true` if `self` has a looser binding than `other`. pub const fn is_looser_than(self, other: Self) -> bool { self.0 > other.0 } } /// Maps a token to its infix operator along with its left and right binding /// ranks: `(left_precedence_rank, operator, right_precedence_rank)`. /// /// Returns [`None`] if and only if `token` is not an infix operator. pub fn infix_precedence_ranks( token: Token, ) -> Option<(PrecedenceRank, InfixOperator, PrecedenceRank)> { let info = infix_operator_info(token)?; // All operators are left-associative, so `right_precedence_rank` is set to // `left_binding_rank - 1` (with our "smaller is tighter" scale, this // enforces left associativity in Pratt parsing). // // Since all precedences are even, subtracting one won't actually cross // any boundary between operator groups. Some(( PrecedenceRank(info.right_precedence_rank), info.operator, PrecedenceRank(info.right_precedence_rank - 1), )) }