Give |> the highest precedence, plus some perf opt

This commit is contained in:
Richard Feldman 2023-03-29 12:28:12 -04:00
parent 305753b4a7
commit 72dc754c28
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B

View file

@ -1,7 +1,79 @@
use self::Associativity::*;
use self::BinOp::*;
use std::cmp::Ordering;
use std::fmt;
const PRECEDENCES: [(BinOp, u8); 20] = [
(Pizza, 6),
(Caret, 5),
(Star, 4),
(Slash, 4),
(DoubleSlash, 4),
(Percent, 4),
(Plus, 3),
(Minus, 3),
(Equals, 2),
(NotEquals, 2),
(LessThan, 1),
(GreaterThan, 1),
(LessThanOrEq, 1),
(GreaterThanOrEq, 1),
(And, 0),
(Or, 0),
// These should never come up
(Assignment, 255),
(IsAliasType, 255),
(IsOpaqueType, 255),
(Backpassing, 255),
];
const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [
(Pizza, LeftAssociative),
(Caret, RightAssociative),
(Star, LeftAssociative),
(Slash, LeftAssociative),
(DoubleSlash, LeftAssociative),
(Percent, LeftAssociative),
(Plus, LeftAssociative),
(Minus, LeftAssociative),
(Equals, NonAssociative),
(NotEquals, NonAssociative),
(LessThan, NonAssociative),
(GreaterThan, NonAssociative),
(LessThanOrEq, NonAssociative),
(GreaterThanOrEq, NonAssociative),
(And, RightAssociative),
(Or, RightAssociative),
// These should never come up
(Assignment, LeftAssociative),
(IsAliasType, LeftAssociative),
(IsOpaqueType, LeftAssociative),
(Backpassing, LeftAssociative),
];
const DISPLAY_STRINGS: [(BinOp, &str); 20] = [
(Pizza, "|>"),
(Caret, "^"),
(Star, "*"),
(Slash, "/"),
(DoubleSlash, "//"),
(Percent, "%"),
(Plus, "+"),
(Minus, "-"),
(Equals, "=="),
(NotEquals, "!="),
(LessThan, "<"),
(GreaterThan, ">"),
(LessThanOrEq, "<="),
(GreaterThanOrEq, ">="),
(And, "&&"),
(Or, "||"),
(Assignment, "="),
(IsAliasType, ":"),
(IsOpaqueType, ":="),
(Backpassing, "<-"),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CalledVia {
/// Calling with space, e.g. (foo bar)
@ -29,6 +101,7 @@ pub enum UnaryOp {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinOp {
// highest precedence
Pizza,
Caret,
Star,
Slash,
@ -44,7 +117,6 @@ pub enum BinOp {
GreaterThanOrEq,
And,
Or,
Pizza,
Assignment,
IsAliasType,
IsOpaqueType,
@ -93,29 +165,27 @@ pub enum Associativity {
impl BinOp {
pub fn associativity(self) -> Associativity {
use self::Associativity::*;
// The compiler should never pass any of these to this function!
debug_assert_ne!(self, Assignment);
debug_assert_ne!(self, IsAliasType);
debug_assert_ne!(self, IsOpaqueType);
debug_assert_ne!(self, Backpassing);
match self {
Pizza | Star | Slash | DoubleSlash | Percent | Plus | Minus => LeftAssociative,
And | Or | Caret => RightAssociative,
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
NonAssociative
}
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
}
const ASSOCIATIVITY_TABLE: [Associativity; 20] = generate_associativity_table();
ASSOCIATIVITY_TABLE[self as usize]
}
fn precedence(self) -> u8 {
match self {
Caret => 7,
Star | Slash | DoubleSlash | Percent => 6,
Plus | Minus => 5,
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
And => 3,
Or => 2,
Pizza => 1,
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
}
// The compiler should never pass any of these to this function!
debug_assert_ne!(self, Assignment);
debug_assert_ne!(self, IsAliasType);
debug_assert_ne!(self, IsOpaqueType);
debug_assert_ne!(self, Backpassing);
const PRECEDENCE_TABLE: [u8; 20] = generate_precedence_table();
PRECEDENCE_TABLE[self as usize]
}
}
@ -133,29 +203,84 @@ impl Ord for BinOp {
impl std::fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let as_str = match self {
Caret => "^",
Star => "*",
Slash => "/",
DoubleSlash => "//",
Percent => "%",
Plus => "+",
Minus => "-",
Equals => "==",
NotEquals => "!=",
LessThan => "<",
GreaterThan => ">",
LessThanOrEq => "<=",
GreaterThanOrEq => ">=",
And => "&&",
Or => "||",
Pizza => "|>",
Assignment => "=",
IsAliasType => ":",
IsOpaqueType => ":=",
Backpassing => "<-",
};
debug_assert_ne!(*self, Assignment);
debug_assert_ne!(*self, IsAliasType);
debug_assert_ne!(*self, IsOpaqueType);
debug_assert_ne!(*self, Backpassing);
write!(f, "{}", as_str)
const DISPLAY_TABLE: [&str; 20] = generate_display_table();
write!(f, "{}", DISPLAY_TABLE[*self as usize])
}
}
const fn generate_precedence_table() -> [u8; 20] {
let mut table = [0u8; 20];
let mut i = 0;
while i < PRECEDENCES.len() {
table[(PRECEDENCES[i].0) as usize] = PRECEDENCES[i].1;
i += 1;
}
table
}
const fn generate_associativity_table() -> [Associativity; 20] {
let mut table = [NonAssociative; 20];
let mut i = 0;
while i < ASSOCIATIVITIES.len() {
table[(ASSOCIATIVITIES[i].0) as usize] = ASSOCIATIVITIES[i].1;
i += 1;
}
table
}
const fn generate_display_table() -> [&'static str; 20] {
let mut table = [""; 20];
let mut i = 0;
while i < DISPLAY_STRINGS.len() {
table[(DISPLAY_STRINGS[i].0) as usize] = DISPLAY_STRINGS[i].1;
i += 1;
}
table
}
#[cfg(test)]
mod tests {
use super::{BinOp, ASSOCIATIVITIES, DISPLAY_STRINGS, PRECEDENCES};
fn no_duplicates(iter: impl Iterator<Item = BinOp> + Clone, table_name: &'static str) {
for op_to_count in iter.clone() {
let mut instances = 0;
for current_op in iter.clone() {
if current_op == op_to_count {
instances += 1
}
}
// Each op should appear exactly once in the table
assert_eq!(instances, 1, "{op_to_count} appeared {instances} times in {table_name}, but we expected it to appear once at most.");
}
}
#[test]
fn no_duplicates_in_precedences() {
no_duplicates(PRECEDENCES.iter().map(|(op, _)| *op), "PRECEDENCES")
}
#[test]
fn no_duplicates_in_associativities() {
no_duplicates(ASSOCIATIVITIES.iter().map(|(op, _)| *op), "ASSOCIATIVITIES")
}
#[test]
fn no_duplicates_in_display_strings() {
no_duplicates(DISPLAY_STRINGS.iter().map(|(op, _)| *op), "DISPLAY_STRINGS")
}
}