Skip to content

Commit

Permalink
Add optional typenum-macro that provides tyint and tyuint
Browse files Browse the repository at this point in the history
Both tyint tyuint are convenient macros to convert integer
literals into typenum types.
  • Loading branch information
jerry73204 committed Jul 24, 2020
1 parent 3589cd4 commit 4ed5413
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
description = "Typenum is a Rust library for type-level numbers evaluated at compile time. It currently supports bits, unsigned integers, and signed integers. It also provides a type-level array of type-level numbers, but its implementation is incomplete."
categories = ["no-std"]

[dependencies]
typenum-macro = { version = "0.1.0", path = "typenum-macro" }

[lib]
name = "typenum"

Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
)]
#![cfg_attr(feature = "cargo-clippy", deny(clippy::missing_inline_in_public_items))]

extern crate typenum_macro;

// For debugging macros:
// #![feature(trace_macros)]
// trace_macros!(true);
Expand All @@ -84,6 +86,8 @@ pub mod private;
pub mod type_operators;
pub mod uint;

mod macros;

pub mod array;

pub use consts::*;
Expand All @@ -96,6 +100,8 @@ pub use array::{ATerm, TArr};
pub use int::{NInt, PInt};
pub use uint::{UInt, UTerm};

pub use macros::*;

/// A potential output from `Cmp`, this is the type equivalent to the enum variant
/// `core::cmp::Ordering::Greater`.
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Debug, Default)]
Expand Down
25 changes: 25 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// Generates a signed typenum from a signed integer literal.
///
/// The output type can be either [PInt](typenum::PInt), [NInt](typenum::NInt) or [Z0](typenum::Z0),
/// (e.g., [P1](typenum::P1), [N2](typenum::N2)) depending on the value of input literal.
///
/// ```rust
/// use typenum::{tyint, N2, P1, Z0};
/// let _: tyint!(0) = Z0::new();
/// let _: tyint!(1) = P1::new();
/// let _: tyint!(-2) = N2::new();
/// ```
pub use typenum_macro::tyint;

/// Generates an unsigned typenum from an unsigned integer literal.
///
/// The output type can be either [UTerm](typenum::UTerm) or [UInt](typenum::UInt)
/// (e.g., [U0](typenum::U0), [U1](typenum::U1)) depending on the value of input literal.
///
/// ```rust
/// use typenum::{tyuint, U0, U1, U2};
/// let _: tyuint!(0) = U0::new();
/// let _: tyuint!(1) = U1::new();
/// let _: tyuint!(2) = U2::new();
/// ```
pub use typenum_macro::tyuint;
67 changes: 67 additions & 0 deletions tests/tyint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#![cfg(feature = "macros")]
#![no_std]

extern crate typenum;

use core::marker::PhantomData;
use typenum::{
consts::*,
macros::{tyint, tyuint},
};

struct Same<Lhs, Rhs> {
_phantom: PhantomData<(Lhs, Rhs)>,
}

impl<T> Same<T, T> {
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}

type Positive0 = tyint!(0);
type Positive1 = tyint!(1);
type Positive2 = tyint!(2);
type Positive3 = tyint!(3);
type Positive4 = tyint!(4);
type Positive4294967296 = tyint!(4294967296);

type Negative0 = tyint!(-0);
type Negative1 = tyint!(-1);
type Negative2 = tyint!(-2);
type Negative3 = tyint!(-3);
type Negative4 = tyint!(-4);
type Negative4294967296 = tyint!(-4294967296);

type Unsigned0 = tyuint!(0);
type Unsigned1 = tyuint!(1);
type Unsigned2 = tyuint!(2);
type Unsigned3 = tyuint!(3);
type Unsigned4 = tyuint!(4);
type Unsigned4294967296 = tyuint!(4294967296);

#[test]
fn tyint_test() {
let _ = Same::<Positive0, Z0>::new();
let _ = Same::<Positive1, P1>::new();
let _ = Same::<Positive2, P2>::new();
let _ = Same::<Positive3, P3>::new();
let _ = Same::<Positive4, P4>::new();
let _ = Same::<Positive4294967296, P4294967296>::new();

let _ = Same::<Negative0, Z0>::new();
let _ = Same::<Negative1, N1>::new();
let _ = Same::<Negative2, N2>::new();
let _ = Same::<Negative3, N3>::new();
let _ = Same::<Negative4, N4>::new();
let _ = Same::<Negative4294967296, N4294967296>::new();

let _ = Same::<Unsigned0, U0>::new();
let _ = Same::<Unsigned1, U1>::new();
let _ = Same::<Unsigned2, U2>::new();
let _ = Same::<Unsigned3, U3>::new();
let _ = Same::<Unsigned4, U4>::new();
let _ = Same::<Unsigned4294967296, U4294967296>::new();
}
13 changes: 13 additions & 0 deletions typenum-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "typenum-macro"
version = "0.1.0"
authors = ["jerry73204 <[email protected]>"]
edition = "2018"

[lib]
proc-macro = true

[dependencies]
syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
104 changes: 104 additions & 0 deletions typenum-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#![no_std]

extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
parse::{Parse, ParseStream, Result as ParseResult},
parse_macro_input, LitInt, Token,
};

struct SignedInteger {
has_negative_op: bool,
value: u128,
}

impl Parse for SignedInteger {
fn parse(input: ParseStream) -> ParseResult<Self> {
let has_negative_op = if input.peek(Token![-]) {
input.parse::<Token![-]>()?;
true
} else {
false
};

let literal = input.parse::<LitInt>()?;
let value = literal.base10_parse::<u128>()?;

let output = SignedInteger {
has_negative_op,
value,
};

Ok(output)
}
}

struct UnsignedInteger {
value: u128,
}

impl Parse for UnsignedInteger {
fn parse(input: ParseStream) -> ParseResult<Self> {
let literal = input.parse::<LitInt>()?;
let value = literal.base10_parse::<u128>()?;

let output = UnsignedInteger { value };

Ok(output)
}
}

#[proc_macro]
pub fn tyint(input: TokenStream) -> TokenStream {
let SignedInteger {
has_negative_op,
value,
} = parse_macro_input!(input as SignedInteger);

let tokens = if value == 0 {
quote! {
typenum::consts::Z0
}
} else if has_negative_op {
let uint_tokens = recursive_value_to_typeuint(value);
quote! {
typenum::int::NInt<#uint_tokens>
}
} else {
let uint_tokens = recursive_value_to_typeuint(value);
quote! {
typenum::int::PInt<#uint_tokens>
}
};

TokenStream::from(tokens)
}

#[proc_macro]
pub fn tyuint(input: TokenStream) -> TokenStream {
let UnsignedInteger { value } = parse_macro_input!(input as UnsignedInteger);

let tokens = recursive_value_to_typeuint(value);
TokenStream::from(tokens)
}

fn recursive_value_to_typeuint(value: u128) -> TokenStream2 {
if value == 0 {
quote! {
typenum::uint::UTerm
}
} else if value & 1 == 1 {
let sub_tokens = recursive_value_to_typeuint(value >> 1);
quote! {
typenum::uint::UInt<#sub_tokens, typenum::bit::B1>
}
} else {
let sub_tokens = recursive_value_to_typeuint(value >> 1);
quote! {
typenum::uint::UInt<#sub_tokens, typenum::bit::B0>
}
}
}

0 comments on commit 4ed5413

Please sign in to comment.