diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/typenum/build/op.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/typenum/build/op.rs')
-rw-r--r-- | vendor/typenum/build/op.rs | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/vendor/typenum/build/op.rs b/vendor/typenum/build/op.rs new file mode 100644 index 000000000..756f37229 --- /dev/null +++ b/vendor/typenum/build/op.rs @@ -0,0 +1,559 @@ +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum OpType { + Operator, + Function, +} + +use self::OpType::*; + +struct Op { + token: &'static str, + operator: &'static str, + example: (&'static str, &'static str), + precedence: u8, + n_args: u8, + op_type: OpType, +} + +pub fn write_op_macro() -> ::std::io::Result<()> { + let out_dir = ::std::env::var("OUT_DIR").unwrap(); + let dest = ::std::path::Path::new(&out_dir).join("op.rs"); + println!("cargo:rustc-env=TYPENUM_BUILD_OP={}", dest.display()); + let mut f = ::std::fs::File::create(&dest).unwrap(); + + // Operator precedence is taken from + // https://doc.rust-lang.org/reference.html#operator-precedence + // + // We choose 16 as the highest precedence (functions are set to 255 but it doesn't matter + // for them). We also only use operators that are left associative so we don't have to worry + // about that. + let ops = &[ + Op { + token: "*", + operator: "Prod", + example: ("P2 * P3", "P6"), + precedence: 16, + n_args: 2, + op_type: Operator, + }, + Op { + token: "/", + operator: "Quot", + example: ("P6 / P2", "P3"), + precedence: 16, + n_args: 2, + op_type: Operator, + }, + Op { + token: "%", + operator: "Mod", + example: ("P5 % P3", "P2"), + precedence: 16, + n_args: 2, + op_type: Operator, + }, + Op { + token: "+", + operator: "Sum", + example: ("P2 + P3", "P5"), + precedence: 15, + n_args: 2, + op_type: Operator, + }, + Op { + token: "-", + operator: "Diff", + example: ("P2 - P3", "N1"), + precedence: 15, + n_args: 2, + op_type: Operator, + }, + Op { + token: "<<", + operator: "Shleft", + example: ("U1 << U5", "U32"), + precedence: 14, + n_args: 2, + op_type: Operator, + }, + Op { + token: ">>", + operator: "Shright", + example: ("U32 >> U5", "U1"), + precedence: 14, + n_args: 2, + op_type: Operator, + }, + Op { + token: "&", + operator: "And", + example: ("U5 & U3", "U1"), + precedence: 13, + n_args: 2, + op_type: Operator, + }, + Op { + token: "^", + operator: "Xor", + example: ("U5 ^ U3", "U6"), + precedence: 12, + n_args: 2, + op_type: Operator, + }, + Op { + token: "|", + operator: "Or", + example: ("U5 | U3", "U7"), + precedence: 11, + n_args: 2, + op_type: Operator, + }, + Op { + token: "==", + operator: "Eq", + example: ("P5 == P3 + P2", "True"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "!=", + operator: "NotEq", + example: ("P5 != P3 + P2", "False"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "<=", + operator: "LeEq", + example: ("P6 <= P3 + P2", "False"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: ">=", + operator: "GrEq", + example: ("P6 >= P3 + P2", "True"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "<", + operator: "Le", + example: ("P4 < P3 + P2", "True"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: ">", + operator: "Gr", + example: ("P5 < P3 + P2", "False"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "cmp", + operator: "Compare", + example: ("cmp(P2, P3)", "Less"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "sqr", + operator: "Square", + example: ("sqr(P2)", "P4"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "sqrt", + operator: "Sqrt", + example: ("sqrt(U9)", "U3"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "abs", + operator: "AbsVal", + example: ("abs(N2)", "P2"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "cube", + operator: "Cube", + example: ("cube(P2)", "P8"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "pow", + operator: "Exp", + example: ("pow(P2, P3)", "P8"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "min", + operator: "Minimum", + example: ("min(P2, P3)", "P2"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "max", + operator: "Maximum", + example: ("max(P2, P3)", "P3"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "log2", + operator: "Log2", + example: ("log2(U9)", "U3"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "gcd", + operator: "Gcf", + example: ("gcd(U9, U21)", "U3"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + ]; + + use std::io::Write; + write!( + f, + " +/** +Convenient type operations. + +Any types representing values must be able to be expressed as `ident`s. That means they need to be +in scope. + +For example, `P5` is okay, but `typenum::P5` is not. + +You may combine operators arbitrarily, although doing so excessively may require raising the +recursion limit. + +# Example +```rust +#![recursion_limit=\"128\"] +#[macro_use] extern crate typenum; +use typenum::consts::*; + +fn main() {{ + assert_type!( + op!(min((P1 - P2) * (N3 + N7), P5 * (P3 + P4)) == P10) + ); +}} +``` +Operators are evaluated based on the operator precedence outlined +[here](https://doc.rust-lang.org/reference.html#operator-precedence). + +The full list of supported operators and functions is as follows: + +{} + +They all expand to type aliases defined in the `operator_aliases` module. Here is an expanded list, +including examples: + +", + ops.iter() + .map(|op| format!("`{}`", op.token)) + .collect::<Vec<_>>() + .join(", ") + )?; + + //write!(f, "Token | Alias | Example\n ===|===|===\n")?; + + for op in ops.iter() { + write!( + f, + "---\nOperator `{token}`. Expands to `{operator}`. + +```rust +# #[macro_use] extern crate typenum; +# use typenum::*; +# fn main() {{ +assert_type_eq!(op!({ex0}), {ex1}); +# }} +```\n +", + token = op.token, + operator = op.operator, + ex0 = op.example.0, + ex1 = op.example.1 + )?; + } + + write!( + f, + "*/ +#[macro_export(local_inner_macros)] +macro_rules! op {{ + ($($tail:tt)*) => ( __op_internal__!($($tail)*) ); +}} + + #[doc(hidden)] + #[macro_export(local_inner_macros)] + macro_rules! __op_internal__ {{ +" + )?; + + // We first us the shunting-yard algorithm to produce our tokens in Polish notation. + // See: https://en.wikipedia.org/wiki/Shunting-yard_algorithm + + // Note: Due to macro asymmetry, "the top of the stack" refers to the first element, not the + // last + + // ----------------------------------------------------------------------------------------- + // Stage 1: There are tokens to be read: + + // ------- + // Case 1: Token is a function => Push it onto the stack: + for fun in ops.iter().filter(|f| f.op_type == Function) { + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: {f_token} $($tail:tt)*) => ( + __op_internal__!(@stack[{f_op}, $($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);", + f_token = fun.token, + f_op = fun.operator + )?; + } + + // ------- + // Case 2: Token is a comma => Until the top of the stack is a LParen, + // Pop operators from stack to queue + + // Base case: Top of stack is LParen, ditch comma and continue + write!( + f, + " +(@stack[LParen, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: , $($tail:tt)*) => ( + __op_internal__!(@stack[LParen, $($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);" + )?; + // Recursive case: Not LParen, pop from stack to queue + write!( + f, + " +(@stack[$stack_top:ident, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: , $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$stack_top, $($queue,)*] @tail: , $($tail)*) +);" + )?; + + // ------- + // Case 3: Token is an operator, o1: + for o1 in ops.iter().filter(|op| op.op_type == Operator) { + // If top of stack is operator o2 with o1.precedence <= o2.precedence, + // Then pop o2 off stack onto queue: + for o2 in ops + .iter() + .filter(|op| op.op_type == Operator) + .filter(|o2| o1.precedence <= o2.precedence) + { + write!( + f, + " +(@stack[{o2_op}, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: {o1_token} $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[{o2_op}, $($queue,)*] @tail: {o1_token} $($tail)*) +);", + o2_op = o2.operator, + o1_token = o1.token + )?; + } + // Base case: push o1 onto stack + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: {o1_token} $($tail:tt)*) => ( + __op_internal__!(@stack[{o1_op}, $($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);", + o1_op = o1.operator, + o1_token = o1.token + )?; + } + + // ------- + // Case 4: Token is "(": push it onto stack as "LParen". Also convert the ")" to "RParen" to + // appease the macro gods: + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: ( $($stuff:tt)* ) $($tail:tt)* ) + => ( + __op_internal__!(@stack[LParen, $($stack,)*] @queue[$($queue,)*] + @tail: $($stuff)* RParen $($tail)*) +);" + )?; + + // ------- + // Case 5: Token is "RParen": + // 1. Pop from stack to queue until we see an "LParen", + // 2. Kill the "LParen", + // 3. If the top of the stack is a function, pop it onto the queue + // 2. Base case: + write!( + f, + " +(@stack[LParen, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: RParen $($tail:tt)*) => ( + __op_internal__!(@rp3 @stack[$($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);" + )?; + // 1. Recursive case: + write!( + f, + " +(@stack[$stack_top:ident, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: RParen $($tail:tt)*) + => ( + __op_internal__!(@stack[$($stack,)*] @queue[$stack_top, $($queue,)*] @tail: RParen $($tail)*) +);" + )?; + // 3. Check for function: + for fun in ops.iter().filter(|f| f.op_type == Function) { + write!( + f, + " +(@rp3 @stack[{fun_op}, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[{fun_op}, $($queue,)*] @tail: $($tail)*) +);", + fun_op = fun.operator + )?; + } + // 3. If no function found: + write!( + f, + " +(@rp3 @stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);" + )?; + + // ------- + // Case 6: Token is a number: Push it onto the queue + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: $num:ident $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$num, $($queue,)*] @tail: $($tail)*) +);" + )?; + + // ------- + // Case 7: Out of tokens: + // Base case: Stack empty: Start evaluating + write!( + f, + " +(@stack[] @queue[$($queue:ident,)*] @tail: ) => ( + __op_internal__!(@reverse[] @input: $($queue,)*) +);" + )?; + // Recursive case: Pop stack to queue + write!( + f, + " +(@stack[$stack_top:ident, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail:) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$stack_top, $($queue,)*] @tail: ) +);" + )?; + + // ----------------------------------------------------------------------------------------- + // Stage 2: Reverse so we have RPN + write!( + f, + " +(@reverse[$($revved:ident,)*] @input: $head:ident, $($tail:ident,)* ) => ( + __op_internal__!(@reverse[$head, $($revved,)*] @input: $($tail,)*) +);" + )?; + write!( + f, + " +(@reverse[$($revved:ident,)*] @input: ) => ( + __op_internal__!(@eval @stack[] @input[$($revved,)*]) +);" + )?; + + // ----------------------------------------------------------------------------------------- + // Stage 3: Evaluate in Reverse Polish Notation + // Operators / Operators with 2 args: + for op in ops.iter().filter(|op| op.n_args == 2) { + // Note: We have to switch $a and $b here, otherwise non-commutative functions are backwards + write!( + f, + " +(@eval @stack[$a:ty, $b:ty, $($stack:ty,)*] @input[{op}, $($tail:ident,)*]) => ( + __op_internal__!(@eval @stack[$crate::{op}<$b, $a>, $($stack,)*] @input[$($tail,)*]) +);", + op = op.operator + )?; + } + // Operators with 1 arg: + for op in ops.iter().filter(|op| op.n_args == 1) { + write!( + f, + " +(@eval @stack[$a:ty, $($stack:ty,)*] @input[{op}, $($tail:ident,)*]) => ( + __op_internal__!(@eval @stack[$crate::{op}<$a>, $($stack,)*] @input[$($tail,)*]) +);", + op = op.operator + )?; + } + + // Wasn't a function or operator, so must be a value => push onto stack + write!( + f, + " +(@eval @stack[$($stack:ty,)*] @input[$head:ident, $($tail:ident,)*]) => ( + __op_internal__!(@eval @stack[$head, $($stack,)*] @input[$($tail,)*]) +);" + )?; + + // No input left: + write!( + f, + " +(@eval @stack[$stack:ty,] @input[]) => ( + $stack +);" + )?; + + // ----------------------------------------------------------------------------------------- + // Stage 0: Get it started + write!( + f, + " +($($tail:tt)* ) => ( + __op_internal__!(@stack[] @queue[] @tail: $($tail)*) +);" + )?; + + write!( + f, + " +}}" + )?; + + Ok(()) +} |