mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Merge pull request #5230 from roc-lang/binop-revisions
Give |> the highest precedence, plus some perf opt
This commit is contained in:
commit
2910024be5
1 changed files with 159 additions and 43 deletions
|
@ -1,7 +1,79 @@
|
|||
use self::Associativity::*;
|
||||
use self::BinOp::*;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
const PRECEDENCES: [(BinOp, u8); 20] = [
|
||||
(Caret, 7),
|
||||
(Star, 6),
|
||||
(Slash, 6),
|
||||
(DoubleSlash, 5),
|
||||
(Percent, 5),
|
||||
(Plus, 4),
|
||||
(Minus, 4),
|
||||
(Pizza, 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] = [
|
||||
(Caret, RightAssociative),
|
||||
(Star, LeftAssociative),
|
||||
(Slash, LeftAssociative),
|
||||
(DoubleSlash, LeftAssociative),
|
||||
(Percent, LeftAssociative),
|
||||
(Plus, LeftAssociative),
|
||||
(Minus, LeftAssociative),
|
||||
(Pizza, 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] = [
|
||||
(Caret, "^"),
|
||||
(Star, "*"),
|
||||
(Slash, "/"),
|
||||
(DoubleSlash, "//"),
|
||||
(Percent, "%"),
|
||||
(Plus, "+"),
|
||||
(Minus, "-"),
|
||||
(Pizza, "|>"),
|
||||
(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)
|
||||
|
@ -36,6 +108,7 @@ pub enum BinOp {
|
|||
Percent,
|
||||
Plus,
|
||||
Minus,
|
||||
Pizza,
|
||||
Equals,
|
||||
NotEquals,
|
||||
LessThan,
|
||||
|
@ -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,75 @@ 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 index_is_binop_u8(iter: impl Iterator<Item = BinOp>, table_name: &'static str) {
|
||||
for (index, op) in iter.enumerate() {
|
||||
assert_eq!(op as usize, index, "{op} was found at index {index} in {table_name}, but it should have been at index {} instead.", op as usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indices_are_correct_in_precedences() {
|
||||
index_is_binop_u8(PRECEDENCES.iter().map(|(op, _)| *op), "PRECEDENCES")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indices_are_correct_in_associativities() {
|
||||
index_is_binop_u8(ASSOCIATIVITIES.iter().map(|(op, _)| *op), "ASSOCIATIVITIES")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indices_are_correct_in_display_string() {
|
||||
index_is_binop_u8(DISPLAY_STRINGS.iter().map(|(op, _)| *op), "DISPLAY_STRINGS")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue