Initial commit
This commit is contained in:
commit
59c0285103
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.vscode
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "overflow"
|
||||
version = "0.0.1"
|
||||
edition = "2018"
|
||||
autotests = false
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[[test]]
|
||||
name = "tests"
|
||||
path = "tests/progress.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "0.15.39", features = ["full", "extra-traits"] }
|
||||
quote = "0.6"
|
||||
proc-macro2 = "0.4.30"
|
|
@ -0,0 +1,34 @@
|
|||
# Overflow
|
||||
|
||||
A convenience macro for changing the overflow properties of math expressions without having to change those expressions (mostly).
|
||||
|
||||
## Example
|
||||
|
||||
By default the following will fail in dev builds at runtime:
|
||||
|
||||
```rust
|
||||
(2.pow(20) << 20) + 2 * 2
|
||||
```
|
||||
|
||||
In order to make this wrap in dev and release builds you would need to write it this way:
|
||||
|
||||
```rust
|
||||
(2.wrapping_pow(20).wrapping_shl(20)).wrapping_add(2.wrapping_mul(2))
|
||||
```
|
||||
|
||||
Or you could use Overflow and write the following:
|
||||
|
||||
```rust
|
||||
overflow::wrapping { (2.pow(20) << 20) + 2 * 2 }
|
||||
```
|
||||
|
||||
The above converts the normal meth expression syntax directly into the `wrapping` variant from above.
|
||||
|
||||
## Limitations
|
||||
|
||||
Overflow is currently limited in the following:
|
||||
|
||||
* The crate currently requires nightly because proc macros in expressions are not currently stable.
|
||||
* Because math operations can more easily propogate type inference information than method calls, you may have to add type information when using the macros that were not neceesary before.
|
||||
* Overflow behaivor is only affected at the top level (or within parenthesis) meaning that if you have math expressions inside of method invocations or inside of other macros, those expressions will not be converted.
|
||||
* Conversion of `pow` is extremely naive so if you call a `pow` method on some type, this will be converted to `wrapping_pow` even if that makes no sense for that type.
|
|
@ -0,0 +1,72 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use quote::quote;
|
||||
use syn::{spanned::Spanned, BinOp, Expr, ExprBinary, ExprUnary, Ident, UnOp};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn wrapping(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as syn::Expr);
|
||||
let expanded = parse_expr(input);
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn parse_expr(mut expr: syn::Expr) -> proc_macro2::TokenStream {
|
||||
match expr {
|
||||
Expr::Unary(unary) => parse_unary(unary),
|
||||
Expr::Binary(binary) => parse_binary(binary),
|
||||
Expr::MethodCall(ref mut mc) if mc.method == "pow" => {
|
||||
mc.method = syn::Ident::new("wrapping_pow", mc.method.span());
|
||||
quote! { #mc }
|
||||
}
|
||||
Expr::Paren(p) => {
|
||||
let expr = parse_expr(*p.expr);
|
||||
quote! {
|
||||
(#expr)
|
||||
}
|
||||
}
|
||||
_ => quote! { #expr },
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_unary(unary: ExprUnary) -> proc_macro2::TokenStream {
|
||||
let expr = parse_expr(*unary.expr);
|
||||
let op = unary.op;
|
||||
match op {
|
||||
UnOp::Neg(_) => {
|
||||
quote! {
|
||||
#expr.wrapping_neg()
|
||||
}
|
||||
}
|
||||
_ => quote! { #expr },
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_binary(binary: ExprBinary) -> proc_macro2::TokenStream {
|
||||
let left = parse_expr(*binary.left);
|
||||
let right = parse_expr(*binary.right);
|
||||
let op = binary.op;
|
||||
let method_name = match op {
|
||||
BinOp::Add(_) => Some("wrapping_add"),
|
||||
BinOp::Sub(_) => Some("wrapping_sub"),
|
||||
BinOp::Mul(_) => Some("wrapping_mul"),
|
||||
BinOp::Div(_) => Some("wrapping_div"),
|
||||
BinOp::Rem(_) => Some("wrapping_rem"),
|
||||
BinOp::Shl(_) => Some("wrapping_shl"),
|
||||
BinOp::Shr(_) => Some("wrapping_shr"),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
method_name
|
||||
.map(|method_name| {
|
||||
let method_name = Ident::new(method_name, op.span());
|
||||
quote! {
|
||||
#left.#method_name(#right)
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
quote! { #left #op #right }
|
||||
})
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#![feature(proc_macro_hygiene)]
|
||||
use overflow::wrapping;
|
||||
|
||||
fn main() {
|
||||
let num = 2u8;
|
||||
let result = wrapping!{ ((num.pow(20) << 20) + 255) + 2u8 * 2u8 };
|
||||
assert!(result == 3);
|
||||
let result = wrapping!{ -std::i8::MIN };
|
||||
assert!(result == -128);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#[test]
|
||||
fn tests() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/01-success.rs");
|
||||
}
|
Loading…
Reference in New Issue