add procedural macro typeflags
This commit is contained in:
parent
344fba6a3d
commit
dd1e21b97c
|
@ -50,12 +50,27 @@ dependencies = [
|
|||
"spin 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "font8x8"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json"
|
||||
version = "0.12.4"
|
||||
|
@ -117,6 +132,15 @@ dependencies = [
|
|||
"spin 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kxos-rights-proc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kxos-std"
|
||||
version = "0.1.0"
|
||||
|
@ -131,6 +155,20 @@ dependencies = [
|
|||
"xmas-elf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kxos-typeflags"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kxos-typeflags-util"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "kxos-util"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -18,6 +18,9 @@ members = [
|
|||
"kxos-virtio",
|
||||
"kxos-util",
|
||||
"kxos-frame-pod-derive",
|
||||
"kxos-typeflags",
|
||||
"kxos-typeflags-util",
|
||||
"kxos-rights-proc",
|
||||
]
|
||||
|
||||
[package.metadata.bootloader]
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "kxos-typeflags-util"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
|
@ -0,0 +1,21 @@
|
|||
//! define macro assert_type_same
|
||||
|
||||
use crate::same::SameAs;
|
||||
|
||||
pub type AssertTypeSame<Lhs, Rhs> = <Lhs as SameAs<Rhs>>::Output;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_type_same {
|
||||
($lhs:ty, $rhs:ty) => {
|
||||
const _: $crate::assert::AssertTypeSame<$lhs, $rhs> = crate::True;
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn test() {
|
||||
assert_type_same!(u16, u16);
|
||||
assert_type_same!((), ());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
//! Type level bools
|
||||
|
||||
pub use std::ops::BitAnd as And;
|
||||
pub use std::ops::BitOr as Or;
|
||||
pub use std::ops::Not;
|
||||
|
||||
pub trait Bool {}
|
||||
|
||||
/// Type-level "true".
|
||||
pub struct True;
|
||||
|
||||
/// Type-level "false".
|
||||
pub struct False;
|
||||
|
||||
impl Bool for True {}
|
||||
impl Bool for False {}
|
||||
|
||||
impl Not for True {
|
||||
type Output = False;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
unimplemented!("not supposed to be used")
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for False {
|
||||
type Output = True;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
unimplemented!("not supposed to be used")
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bool> And<B> for True {
|
||||
type Output = B;
|
||||
|
||||
fn bitand(self, _rhs: B) -> Self::Output {
|
||||
unimplemented!("not supposed to be used")
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bool> And<B> for False {
|
||||
type Output = False;
|
||||
|
||||
fn bitand(self, _rhs: B) -> Self::Output {
|
||||
unimplemented!("not supposed to be used")
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bool> Or<B> for True {
|
||||
type Output = True;
|
||||
|
||||
fn bitor(self, _rhs: B) -> Self::Output {
|
||||
unimplemented!("not supposed to be used")
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bool> Or<B> for False {
|
||||
type Output = B;
|
||||
|
||||
fn bitor(self, _rhs: B) -> Self::Output {
|
||||
unimplemented!("not supposed to be used")
|
||||
}
|
||||
}
|
||||
|
||||
pub type NotOp<B> = <B as Not>::Output;
|
||||
pub type AndOp<B0, B1> = <B0 as And<B1>>::Output;
|
||||
pub type OrOp<B0, B1> = <B0 as Or<B1>>::Output;
|
||||
|
||||
// In where clause, we can only use trait bounds, but not equal bounds.
|
||||
// For certain situation, we need to do some comparison in where clause.
|
||||
// e.g., we need to determine the result type of `SetContainOp` is `True` or `False`.
|
||||
// Since Sometype == True is not allowed, We can use SomeType: IsTrue to determine the result.
|
||||
pub trait IsTrue {}
|
||||
pub trait IsFalse {}
|
||||
|
||||
impl IsTrue for True {}
|
||||
impl IsFalse for False {}
|
|
@ -0,0 +1,29 @@
|
|||
//! Type Level If
|
||||
|
||||
use crate::bool::{False, True};
|
||||
|
||||
pub trait If<B1, B2> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
impl<B1, B2> If<B1, B2> for True {
|
||||
type Output = B1;
|
||||
}
|
||||
|
||||
impl<B1, B2> If<B1, B2> for False {
|
||||
type Output = B2;
|
||||
}
|
||||
|
||||
pub type IfOp<Cond, B1, B2> = <Cond as If<B1, B2>>::Output;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
assert_type_same!(IfOp<True, u32, ()>, u32);
|
||||
assert_type_same!(IfOp<False, (), usize>, usize);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//! The content of this crate is from another project CapComp.
|
||||
//! This crate defines common type level operations, like SameAsOp, and Bool type operations.
|
||||
//! Besides, this crate defines operations to deal with type sets, like SetContain and SetInclude.
|
||||
//! When use kxos-typeflags or kxos-rights-poc, this crate should also be added as a dependency.
|
||||
|
||||
pub mod assert;
|
||||
pub mod bool;
|
||||
pub mod if_;
|
||||
pub mod same;
|
||||
pub mod set;
|
||||
|
||||
pub use crate::bool::{And, AndOp, False, Not, NotOp, Or, OrOp, True, IsFalse, IsTrue};
|
||||
pub use crate::same::{SameAs, SameAsOp};
|
||||
pub use crate::set::{Cons, Nil, Set, SetContain, SetContainOp, SetInclude, SetIncludeOp};
|
||||
pub use assert::AssertTypeSame;
|
|
@ -0,0 +1,28 @@
|
|||
//! Traits used to check if two types are the same, returning a Bool.
|
||||
//! This check happens at compile time
|
||||
|
||||
use crate::bool::{Bool, False, True};
|
||||
|
||||
pub trait SameAs<T> {
|
||||
type Output: Bool;
|
||||
}
|
||||
|
||||
// A type is always same as itself
|
||||
impl<T> SameAs<T> for T {
|
||||
type Output = True;
|
||||
}
|
||||
|
||||
impl SameAs<False> for True {
|
||||
type Output = False;
|
||||
}
|
||||
|
||||
impl SameAs<True> for False {
|
||||
type Output = False;
|
||||
}
|
||||
|
||||
// How to implement self reflection?
|
||||
// impl <T, U> SameAs<T> for U where T: SameAs<U>, {
|
||||
// type Output = <U as SameAs<T>>::Output;
|
||||
// }
|
||||
|
||||
pub type SameAsOp<T, U> = <T as SameAs<U>>::Output;
|
|
@ -0,0 +1,107 @@
|
|||
//! Common types and traits to deal with type-level sets
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
False, OrOp, True,
|
||||
SameAs, SameAsOp,
|
||||
And, AndOp,
|
||||
if_::{If, IfOp},
|
||||
};
|
||||
|
||||
use std::ops::BitOr as Or;
|
||||
|
||||
/// A marker trait for type-level sets.
|
||||
pub trait Set {}
|
||||
|
||||
/// An non-empty type-level set.
|
||||
pub struct Cons<T, S: Set>(PhantomData<(T, S)>);
|
||||
|
||||
impl<T, S: Set> Cons<T, S> {
|
||||
pub fn new() -> Self {
|
||||
Cons(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// An empty type-level set.
|
||||
pub struct Nil;
|
||||
|
||||
impl<T, S: Set> Set for Cons<T, S> {}
|
||||
impl Set for Nil {}
|
||||
|
||||
/// A trait operator to check if `T` is a member of a type set;
|
||||
pub trait SetContain<T> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
pub type SetContainOp<Set, Item> = <Set as SetContain<Item>>::Output;
|
||||
|
||||
impl<T> SetContain<T> for Nil {
|
||||
type Output = False;
|
||||
}
|
||||
|
||||
impl<T, U, S> SetContain<T> for Cons<U, S>
|
||||
where
|
||||
S: Set + SetContain<T>,
|
||||
U: SameAs<T>,
|
||||
SameAsOp<U, T>: Or<SetContainOp<S, T>>,
|
||||
{
|
||||
type Output = OrOp<SameAsOp<U, T>, SetContainOp<S, T>>;
|
||||
}
|
||||
|
||||
/// A trait operator to check if a set A includes a set B, i.e., A is a superset of B.
|
||||
pub trait SetInclude<S: Set> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
pub type SetIncludeOp<Super, Sub> = <Super as SetInclude<Sub>>::Output;
|
||||
|
||||
impl SetInclude<Nil> for Nil {
|
||||
type Output = True;
|
||||
}
|
||||
|
||||
impl<T, S: Set> SetInclude<Cons<T, S>> for Nil {
|
||||
type Output = False;
|
||||
}
|
||||
|
||||
impl<T, S: Set> SetInclude<Nil> for Cons<T, S> {
|
||||
type Output = True;
|
||||
}
|
||||
|
||||
impl<SuperT, SuperS, SubT, SubS> SetInclude<Cons<SubT, SubS>> for Cons<SuperT, SuperS>
|
||||
where
|
||||
SubS: Set,
|
||||
SuperS: Set + SetInclude<SubS> + SetContain<SubT>,
|
||||
SuperT: SameAs<SubT>,
|
||||
SetContainOp<SuperS, SubT>: And<SetIncludeOp<SuperS, SubS>>,
|
||||
SameAsOp<SuperT, SubT>: If<
|
||||
SetIncludeOp<SuperS, SubS>,
|
||||
AndOp<SetContainOp<SuperS, SubT>, SetIncludeOp<SuperS, SubS>>,
|
||||
>,
|
||||
{
|
||||
type Output = IfOp<
|
||||
SameAsOp<SuperT, SubT>,
|
||||
SetIncludeOp<SuperS, SubS>,
|
||||
AndOp<SetContainOp<SuperS, SubT>, SetIncludeOp<SuperS, SubS>>,
|
||||
>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
assert_type_same!(SetContainOp<Cons<True, Nil>, False>, False);
|
||||
assert_type_same!(SetContainOp<Cons<True, Nil>, True>, True);
|
||||
|
||||
assert_type_same!(
|
||||
SetIncludeOp<Cons<True, Cons<True, Nil>>, Cons<True, Nil>>,
|
||||
True
|
||||
);
|
||||
assert_type_same!(
|
||||
SetIncludeOp<Cons<True, Cons<True, Nil>>, Cons<False, Nil>>,
|
||||
False
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "kxos-typeflags"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = {version = "1.0.90"}
|
||||
itertools = "0.10.5"
|
|
@ -0,0 +1,163 @@
|
|||
use itertools::Itertools;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{quote, TokenStreamExt};
|
||||
use syn::Expr;
|
||||
|
||||
use crate::type_flag::TypeFlagDef;
|
||||
|
||||
const EMPTY_SET_NAME: &'static str = "::kxos_typeflags_util::Nil";
|
||||
const SET_NAME: &'static str = "::kxos_typeflags_util::Cons";
|
||||
|
||||
/// A flagSet represent the combination of differnt flag item.
|
||||
/// e.g. [Read, Write], [Read], [] are all flag sets.
|
||||
/// The order of flagItem does not matters. So flag sets with same sets of items should be viewed as the same set.
|
||||
pub struct FlagSet {
|
||||
items: Vec<FlagItem>,
|
||||
}
|
||||
|
||||
impl FlagSet {
|
||||
/// create a new empty flag set
|
||||
pub fn new() -> Self {
|
||||
FlagSet { items: Vec::new() }
|
||||
}
|
||||
|
||||
/// add a flag item
|
||||
pub fn push_item(&mut self, flag_item: FlagItem) {
|
||||
self.items.push(flag_item);
|
||||
}
|
||||
|
||||
/// the flag set string. debug use.
|
||||
pub fn to_string(&self) -> String {
|
||||
if self.items.len() == 0 {
|
||||
return EMPTY_SET_NAME.to_string();
|
||||
}
|
||||
|
||||
let mut res = EMPTY_SET_NAME.to_string();
|
||||
|
||||
for item in self.items.iter() {
|
||||
let replace_set = format!(
|
||||
"{}<{}, {}>",
|
||||
SET_NAME,
|
||||
item.ident.to_string(),
|
||||
EMPTY_SET_NAME
|
||||
);
|
||||
res = res.replace(EMPTY_SET_NAME, &replace_set);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// the tokens represents the flag set type name
|
||||
pub fn type_name_tokens(&self) -> TokenStream {
|
||||
let mut res = quote!(::kxos_typeflags_util::Nil);
|
||||
|
||||
for item in self.items.iter() {
|
||||
let ident = item.ident.clone();
|
||||
|
||||
// insert new item as head
|
||||
let new_res = quote! {
|
||||
::kxos_typeflags_util::Cons<#ident, #res>
|
||||
};
|
||||
res = new_res;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// the number of items
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
/// the tokens to impl main trait for the current flagset
|
||||
pub fn impl_main_trait_tokens(&self, type_flags_def: &TypeFlagDef) -> TokenStream {
|
||||
let trait_ident = type_flags_def.trait_ident();
|
||||
let name = self.type_name_tokens();
|
||||
let mut all_tokens = quote! (
|
||||
impl #trait_ident for #name
|
||||
);
|
||||
all_tokens.append_all(self.inner_tokens(type_flags_def));
|
||||
all_tokens
|
||||
}
|
||||
|
||||
/// the impl main trait inner content
|
||||
fn inner_tokens(&self, type_flags_def: &TypeFlagDef) -> TokenStream {
|
||||
let ty = type_flags_def.val_type();
|
||||
let item_vals = self.items.iter().map(|item| item.val.clone());
|
||||
|
||||
// quote seems unable to use string for types.
|
||||
// So we hardcode all types here.
|
||||
if item_vals.len() == 0 {
|
||||
quote!(
|
||||
{
|
||||
const BITS: #ty = 0 ;
|
||||
fn new() -> Self {
|
||||
::kxos_typeflags_util::Nil
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
quote!(
|
||||
{
|
||||
const BITS: #ty = #(#item_vals)|* ;
|
||||
fn new() -> Self {
|
||||
::kxos_typeflags_util::Cons::new()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The token stream inside macro definition. We will generate a token stream for each permutation of items
|
||||
/// since the user may use arbitrary order of items in macro.
|
||||
pub fn macro_item_tokens(&self) -> Vec<TokenStream> {
|
||||
let res_type = self.type_name_tokens();
|
||||
// We first calculate the full permutations,
|
||||
let item_permutations = self.items.iter().permutations(self.items.len());
|
||||
item_permutations
|
||||
.map(|flag_items| {
|
||||
let item_names = flag_items
|
||||
.into_iter()
|
||||
.map(|flag_item| flag_item.ident.clone())
|
||||
.collect::<Vec<_>>();
|
||||
quote! (
|
||||
(#(#item_names),*) => { #res_type }
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FlagItem {
|
||||
/// the user provided name
|
||||
ident: Ident,
|
||||
/// the user-provided val
|
||||
val: Expr,
|
||||
}
|
||||
|
||||
/// generate all possible flag sets
|
||||
pub fn generate_flag_sets(type_flag_def: &TypeFlagDef) -> Vec<FlagSet> {
|
||||
let flag_items = type_flag_def
|
||||
.items_iter()
|
||||
.map(|type_flag_item| {
|
||||
let ident = type_flag_item.ident();
|
||||
let val = type_flag_item.val();
|
||||
FlagItem { ident, val }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let flag_item_num = flag_items.len();
|
||||
let limit = 1 << flag_items.len();
|
||||
let mut res = Vec::with_capacity(limit);
|
||||
|
||||
for i in 0..limit {
|
||||
let mut flag_set = FlagSet::new();
|
||||
for j in 0..flag_item_num {
|
||||
if (i >> j) & 0x1 == 1usize {
|
||||
flag_set.push_item(flag_items[j].clone());
|
||||
}
|
||||
}
|
||||
res.push(flag_set);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//!This crate defines the procedural macro typeflags to implement capability for kxos.
|
||||
//! When using this crate, kxos-typeflags-util should also be added as dependency.
|
||||
//! This is due to kxos-typeflgas is a proc-macro crate, which is only allowed to export proc-macro interfaces.
|
||||
//! So we leave the common type-level operations and structures defined in kxos-typeflags-util.
|
||||
//!
|
||||
//! type_flag is used to define another declarive macro to define type set.
|
||||
//! It can be used as the following example.
|
||||
//! ```rust
|
||||
//! type_flags! {
|
||||
//! pub trait RightSet: u32 {
|
||||
//! struct Read = 1 << 1;
|
||||
//! struct Write = 1 << 2;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//! The code will generate a macro with the name as RightSet, we can use this macro to define typesets with different types.
|
||||
//! Usage example:
|
||||
//! ```rust
|
||||
//! type O = RightSet![]; // Nil
|
||||
//! type R = RightSet![Read]; // Cons<Read, Nil>
|
||||
//! type W = RightSet![Write]; // Cons<Write, Nil>
|
||||
//! type RW = RightSet![Read, Write]; // Cons<Write, Cons<Read, Nil>>
|
||||
//! type WR = RightSet![Write, Read]; // Cons<Write, Cons<Read, Nil>>
|
||||
//! ```
|
||||
//!
|
||||
//! Test Example
|
||||
//! ```rust
|
||||
//! use kxos_typeflags_util::*;
|
||||
//! assert_eq!(O::BITS, 0);
|
||||
//! assert_eq!(R::BITS, 2);
|
||||
//! assert_eq!(W::BITS, 4);
|
||||
//! assert_eq!(RW::BITS, 6);
|
||||
//! assert_eq!(WR::BITS, 6);
|
||||
//! assert_type_same!(SameAsOp<Read, Write>, False);
|
||||
//! assert_type_same!(SameAsOp<Write, Write>, True);
|
||||
//! assert_type_same!(SameAsOp<O, Nil>, True);
|
||||
//! assert_type_same!(SameAsOp<RW, WR>, True);
|
||||
//! assert_type_same!(SetContainOp<R, Write>, False);
|
||||
//! assert_type_same!(SetContainOp<RW, Read>, True);
|
||||
//! assert_type_same!(SetContainOp<O, Read>, False);
|
||||
//! assert_type_same!(SetContainOp<R, Read>, True);
|
||||
//! assert_type_same!(SetIncludeOp<RW, R>, True);
|
||||
//! assert_type_same!(SetIncludeOp<R, W>, False);
|
||||
//! assert_type_same!(SetIncludeOp<W, O>, True);
|
||||
//! assert_type_same!(SetIncludeOp<O, R>, False);
|
||||
//! ```
|
||||
|
||||
#![feature(proc_macro_diagnostic)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use syn::parse_macro_input;
|
||||
|
||||
use crate::{type_flag::TypeFlagDef, util::expand_type_flag};
|
||||
|
||||
mod flag_set;
|
||||
mod type_flag;
|
||||
mod util;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn type_flags(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let type_flags_def = parse_macro_input!(input as TypeFlagDef);
|
||||
expand_type_flag(&type_flags_def).into()
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
braced,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Expr, Ident, Token, Type, Visibility,
|
||||
};
|
||||
|
||||
/// The content inside typeflag macro
|
||||
pub struct TypeFlagDef {
|
||||
ident: Ident,
|
||||
vis: Visibility,
|
||||
type_: Type,
|
||||
items: Punctuated<TypeFlagItem, Token![;]>,
|
||||
}
|
||||
|
||||
/// struct item inside typeflag macro
|
||||
#[derive(Clone)]
|
||||
pub struct TypeFlagItem {
|
||||
vis: Visibility,
|
||||
ident: Ident,
|
||||
value: Expr,
|
||||
}
|
||||
|
||||
impl Parse for TypeFlagDef {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let vis: Visibility = input.parse()?;
|
||||
let _: Token![trait] = input.parse()?;
|
||||
let ident: Ident = input.parse()?;
|
||||
let _: Token![:] = input.parse()?;
|
||||
let type_: Type = input.parse()?;
|
||||
// read content inside brace
|
||||
let content;
|
||||
let _ = braced!(content in input);
|
||||
let items = content.parse_terminated(TypeFlagItem::parse)?;
|
||||
|
||||
let res = TypeFlagDef {
|
||||
ident,
|
||||
vis,
|
||||
type_,
|
||||
items,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for TypeFlagItem {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let vis: Visibility = input.parse()?;
|
||||
let _: Token![struct] = input.parse()?;
|
||||
let ident: Ident = input.parse()?;
|
||||
let _: Token![=] = input.parse()?;
|
||||
let value: Expr = input.parse()?;
|
||||
let res = TypeFlagItem { vis, ident, value };
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeFlagDef {
|
||||
/// tokens to define the trait
|
||||
pub fn trait_def_tokens(&self) -> TokenStream {
|
||||
let vis = self.vis.clone();
|
||||
let ident = self.ident.clone();
|
||||
let type_ = self.type_.clone();
|
||||
quote!(
|
||||
#vis trait #ident {
|
||||
const BITS: #type_;
|
||||
|
||||
fn new() -> Self;
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// tokens to define all structs
|
||||
pub fn items_def_tokens(&self) -> Vec<TokenStream> {
|
||||
self.items
|
||||
.iter()
|
||||
.map(|type_flag_item| type_flag_item.item_def_tokens())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// return the items iter
|
||||
pub fn items_iter(&self) -> syn::punctuated::Iter<TypeFlagItem> {
|
||||
self.items.iter()
|
||||
}
|
||||
|
||||
/// the number of items
|
||||
pub fn item_num(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
/// get item at specific position
|
||||
pub fn get_item(&self, index: usize) -> Option<TypeFlagItem> {
|
||||
self.items.iter().nth(index).map(|item| item.clone())
|
||||
}
|
||||
|
||||
/// the trait ident
|
||||
pub fn trait_ident(&self) -> Ident {
|
||||
self.ident.clone()
|
||||
}
|
||||
|
||||
/// the val type
|
||||
pub fn val_type(&self) -> Type {
|
||||
self.type_.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeFlagItem {
|
||||
/// the token stream to define such item
|
||||
fn item_def_tokens(&self) -> TokenStream {
|
||||
let vis = self.vis.clone();
|
||||
let ident = self.ident.clone();
|
||||
quote!(
|
||||
#vis struct #ident {}
|
||||
)
|
||||
}
|
||||
|
||||
/// the item's ident
|
||||
pub fn ident(&self) -> Ident {
|
||||
self.ident.clone()
|
||||
}
|
||||
|
||||
/// the expression of the items's value
|
||||
pub fn val(&self) -> Expr {
|
||||
self.value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeFlagDef {
|
||||
/// Debug use. Print all item's name.
|
||||
pub fn debug(&self) {
|
||||
println!("{}", self.ident.to_string());
|
||||
for type_flag_item in &self.items {
|
||||
println!("{}", type_flag_item.ident.to_string());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, TokenStreamExt};
|
||||
|
||||
use crate::{
|
||||
flag_set::{generate_flag_sets, FlagSet},
|
||||
type_flag::TypeFlagDef,
|
||||
};
|
||||
|
||||
pub fn expand_type_flag(type_flags_def: &TypeFlagDef) -> TokenStream {
|
||||
let mut all_tokens = TokenStream::new();
|
||||
let import_util_tokens = import_util();
|
||||
all_tokens.append_all(import_util_tokens);
|
||||
|
||||
let trait_and_items_def_tokens = trait_and_items_def(type_flags_def);
|
||||
all_tokens.append_all(trait_and_items_def_tokens);
|
||||
|
||||
let impl_same_as_tokens = impl_same_as(type_flags_def);
|
||||
all_tokens.append_all(impl_same_as_tokens);
|
||||
|
||||
let flag_sets = generate_flag_sets(&type_flags_def);
|
||||
flag_sets.iter().for_each(|flag_set| {
|
||||
let impl_main_trait_tokens = flag_set.impl_main_trait_tokens(type_flags_def);
|
||||
all_tokens.append_all(impl_main_trait_tokens);
|
||||
});
|
||||
|
||||
let export_declarive_macro_tokens = export_declarive_macro(type_flags_def, &flag_sets);
|
||||
all_tokens.append_all(export_declarive_macro_tokens);
|
||||
|
||||
all_tokens
|
||||
}
|
||||
|
||||
/// import crate kxos_typeflags_util
|
||||
pub fn import_util() -> TokenStream {
|
||||
quote!(
|
||||
#[macro_use]
|
||||
use ::kxos_typeflags_util::*;
|
||||
)
|
||||
}
|
||||
|
||||
/// define the main trait and all items
|
||||
pub fn trait_and_items_def(type_flags_def: &TypeFlagDef) -> TokenStream {
|
||||
let mut tokens = TokenStream::new();
|
||||
tokens.append_all(type_flags_def.trait_def_tokens());
|
||||
for item_def in type_flags_def.items_def_tokens() {
|
||||
tokens.append_all(item_def);
|
||||
}
|
||||
tokens
|
||||
}
|
||||
|
||||
/// impl SameAs trait for each struct
|
||||
pub fn impl_same_as(type_flags_def: &TypeFlagDef) -> TokenStream {
|
||||
let mut all_tokens = TokenStream::new();
|
||||
let items_num = type_flags_def.item_num();
|
||||
|
||||
for i in 0..items_num {
|
||||
let item1 = type_flags_def.get_item(i).unwrap();
|
||||
for j in 0..items_num {
|
||||
if i == j {
|
||||
// We don't need to impl SameAs for the same type
|
||||
continue;
|
||||
}
|
||||
let item2 = type_flags_def.get_item(j).unwrap();
|
||||
let ident1 = item1.ident();
|
||||
let ident2 = item2.ident();
|
||||
let tokens = quote!(
|
||||
impl ::kxos_typeflags_util::SameAs<#ident1> for #ident2 {
|
||||
type Output = ::kxos_typeflags_util::False;
|
||||
}
|
||||
);
|
||||
all_tokens.append_all(tokens);
|
||||
}
|
||||
}
|
||||
all_tokens
|
||||
}
|
||||
|
||||
/// export the declarive macro
|
||||
pub fn export_declarive_macro(type_flags_def: &TypeFlagDef, flag_sets: &[FlagSet]) -> TokenStream {
|
||||
let macro_ident = type_flags_def.trait_ident();
|
||||
let macro_item_tokens = flag_sets
|
||||
.iter()
|
||||
.map(|flag_set| flag_set.macro_item_tokens())
|
||||
.fold(Vec::new(), |mut left, mut new_item| {
|
||||
left.append(&mut new_item);
|
||||
left
|
||||
});
|
||||
|
||||
let tokens = quote!(
|
||||
#[macro_export]
|
||||
macro_rules! #macro_ident {
|
||||
#(#macro_item_tokens);*
|
||||
}
|
||||
);
|
||||
|
||||
tokens
|
||||
}
|
Loading…
Reference in New Issue