//! Operator AST nodes. //! //! This module defines the prefix, postfix, and infix operator kinds used by //! expression AST nodes. //! //! The enums here represent only the *syntactic operator category* recorded in //! the AST. They do not encode precedence, associativity, overload behavior, //! or token spelling details beyond the normalized operator kind itself. //! Those concerns are handled by the expression parser and precedence tables. use crate::lexer::{Keyword, Token, TokenPosition}; use core::convert::TryFrom; /// Prefix unary operators. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum PrefixOperator { /// Logical negation: `!expr`. Not, /// Arithmetic negation: `-expr`. Minus, /// Unary plus: `+expr`. Plus, /// Bitwise negation: `~expr`. BitwiseNot, /// Prefix increment: `++expr`. Increment, /// Prefix decrement: `--expr`. Decrement, } /// Postfix unary operators. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum PostfixOperator { /// Postfix increment: `expr++`. Increment, /// Postfix decrement: `expr--`. Decrement, } /// Binary / infix operators. /// /// These operators appear between left-hand side and right-hand side operands. /// This enum stores only the normalized AST-level operator kind. /// /// The parser assigns precedence and associativity separately. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum InfixOperator { /// Simple assignment: `left_hand_side = right_hand_side`. Assign, /// Multiplicative assignment: `left_hand_side *= right_hand_side`. MultiplyAssign, /// Division assignment: `left_hand_side /= right_hand_side`. DivideAssign, /// Modulo assignment: `left_hand_side %= right_hand_side`. ModuloAssign, /// Additive assignment: `left_hand_side += right_hand_side`. PlusAssign, /// Subtractive assignment: `left_hand_side -= right_hand_side`. MinusAssign, /// String concatenation assignment: `left_hand_side $= right_hand_side`. ConcatAssign, /// Space-concatenation assignment: `left_hand_side @= right_hand_side`. ConcatSpaceAssign, /// String concatenation without inserted whitespace: /// `left_hand_side $ right_hand_side`. Concat, /// String concatenation with an inserted space: /// `left_hand_side @ right_hand_side`. ConcatSpace, /// Logical conjunction: `left_hand_side && right_hand_side`. And, /// Logical exclusive-or: `left_hand_side ^^ right_hand_side`. Xor, /// Logical disjunction: `left_hand_side || right_hand_side`. Or, /// Bitwise AND: `left_hand_side & right_hand_side`. BitwiseAnd, /// Bitwise OR: `left_hand_side | right_hand_side`. BitwiseOr, /// Bitwise XOR: `left_hand_side ^ right_hand_side`. BitwiseXor, /// Inequality test: `left_hand_side != right_hand_side`. NotEqual, /// Equality test: `left_hand_side == right_hand_side`. Equal, /// Approximate equality test: `left_hand_side ~= right_hand_side`. ApproximatelyEqual, /// Less-than comparison: `left_hand_side < right_hand_side`. Less, /// Less-than-or-equal comparison: `left_hand_side <= right_hand_side`. LessEqual, /// Greater-than comparison: `left_hand_side > right_hand_side`. Greater, /// Greater-than-or-equal comparison: `left_hand_side >= right_hand_side`. GreaterEqual, /// UnrealScript-specific directional comparison: /// `left_hand_side ClockwiseFrom right_hand_side`. ClockwiseFrom, /// Left shift: `left_hand_side << right_hand_side`. LeftShift, /// Logical right shift: `left_hand_side >>> right_hand_side`. LogicalRightShift, /// Arithmetic / ordinary right shift: `left_hand_side >> right_hand_side`. RightShift, /// Addition: `left_hand_side + right_hand_side`. Plus, /// Subtraction: `left_hand_side - right_hand_side`. Minus, /// Remainder / modulo: `left_hand_side % right_hand_side`. Modulo, /// Multiplication: `left_hand_side * right_hand_side`. Multiply, /// Division: `left_hand_side / right_hand_side`. Divide, /// Dot product: `left_hand_side Dot right_hand_side`. /// /// This is spelled as a keyword-level operator in source. Dot, /// Cross product: `left_hand_side Cross right_hand_side`. /// /// This is spelled as a keyword-level operator in source. Cross, /// Exponentiation: `left_hand_side ** right_hand_side`. Exponentiation, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PrefixOperatorName { pub kind: PrefixOperator, pub position: TokenPosition, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct InfixOperatorName { pub kind: InfixOperator, pub position: TokenPosition, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PostfixOperatorName { pub kind: PostfixOperator, pub position: TokenPosition, } impl TryFrom for PostfixOperator { type Error = (); fn try_from(token: Token) -> Result { use PostfixOperator::{Decrement, Increment}; match token { Token::Increment => Ok(Increment), Token::Decrement => Ok(Decrement), _ => Err(()), } } } impl TryFrom for PrefixOperator { type Error = (); fn try_from(token: Token) -> Result { use PrefixOperator::{BitwiseNot, Decrement, Increment, Minus, Not, Plus}; match token { Token::Not => Ok(Not), Token::Minus => Ok(Minus), Token::Plus => Ok(Plus), Token::BitwiseNot => Ok(BitwiseNot), Token::Increment => Ok(Increment), Token::Decrement => Ok(Decrement), _ => Err(()), } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) struct InfixOperatorInfo { pub operator: InfixOperator, pub right_precedence_rank: u8, } pub(crate) const fn infix_operator_info(token: Token) -> Option { use InfixOperator::{ And, ApproximatelyEqual, Assign, BitwiseAnd, BitwiseOr, BitwiseXor, ClockwiseFrom, Concat, ConcatAssign, ConcatSpace, ConcatSpaceAssign, Cross, Divide, DivideAssign, Dot, Equal, Exponentiation, Greater, GreaterEqual, LeftShift, Less, LessEqual, LogicalRightShift, Minus, MinusAssign, Modulo, ModuloAssign, Multiply, MultiplyAssign, NotEqual, Or, Plus, PlusAssign, RightShift, Xor, }; let (precedence_rank, operator) = match token { Token::Exponentiation => (12, Exponentiation), Token::Multiply => (16, Multiply), Token::Divide => (16, Divide), Token::Keyword(Keyword::Cross) => (16, Cross), Token::Keyword(Keyword::Dot) => (16, Dot), Token::Modulo => (18, Modulo), Token::Plus => (20, Plus), Token::Minus => (20, Minus), Token::LeftShift => (22, LeftShift), Token::RightShift => (22, RightShift), Token::LogicalRightShift => (22, LogicalRightShift), Token::Less => (24, Less), Token::LessEqual => (24, LessEqual), Token::Greater => (24, Greater), Token::GreaterEqual => (24, GreaterEqual), Token::Equal => (24, Equal), Token::ApproximatelyEqual => (24, ApproximatelyEqual), Token::Keyword(Keyword::ClockwiseFrom) => (24, ClockwiseFrom), Token::NotEqual => (26, NotEqual), Token::BitwiseAnd => (28, BitwiseAnd), Token::BitwiseXor => (28, BitwiseXor), Token::BitwiseOr => (28, BitwiseOr), Token::LogicalAnd => (30, And), Token::LogicalXor => (30, Xor), Token::LogicalOr => (32, Or), Token::MultiplyAssign => (34, MultiplyAssign), Token::DivideAssign => (34, DivideAssign), Token::PlusAssign => (34, PlusAssign), Token::MinusAssign => (34, MinusAssign), Token::Assign => (34, Assign), Token::ModuloAssign => (34, ModuloAssign), Token::Concat => (40, Concat), Token::ConcatSpace => (40, ConcatSpace), Token::ConcatAssign => (44, ConcatAssign), Token::ConcatSpaceAssign => (44, ConcatSpaceAssign), _ => return None, }; Some(InfixOperatorInfo { operator, right_precedence_rank: precedence_rank, }) } impl TryFrom for InfixOperator { type Error = (); fn try_from(token: Token) -> Result { infix_operator_info(token) .map(|info| info.operator) .ok_or(()) } }