mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 13:26:48 +00:00
add procedural macro typeflags
This commit is contained in:
parent
344fba6a3d
commit
dd1e21b97c
38
src/Cargo.lock
generated
38
src/Cargo.lock
generated
@ -50,12 +50,27 @@ dependencies = [
|
|||||||
"spin 0.7.1",
|
"spin 0.7.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "font8x8"
|
name = "font8x8"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
|
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "json"
|
name = "json"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
@ -117,6 +132,15 @@ dependencies = [
|
|||||||
"spin 0.9.4",
|
"spin 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kxos-rights-proc"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kxos-std"
|
name = "kxos-std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -131,6 +155,20 @@ dependencies = [
|
|||||||
"xmas-elf",
|
"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]]
|
[[package]]
|
||||||
name = "kxos-util"
|
name = "kxos-util"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -18,6 +18,9 @@ members = [
|
|||||||
"kxos-virtio",
|
"kxos-virtio",
|
||||||
"kxos-util",
|
"kxos-util",
|
||||||
"kxos-frame-pod-derive",
|
"kxos-frame-pod-derive",
|
||||||
|
"kxos-typeflags",
|
||||||
|
"kxos-typeflags-util",
|
||||||
|
"kxos-rights-proc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.bootloader]
|
[package.metadata.bootloader]
|
||||||
|
8
src/kxos-typeflags-util/Cargo.toml
Normal file
8
src/kxos-typeflags-util/Cargo.toml
Normal file
@ -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]
|
21
src/kxos-typeflags-util/src/assert.rs
Normal file
21
src/kxos-typeflags-util/src/assert.rs
Normal file
@ -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!((), ());
|
||||||
|
}
|
||||||
|
}
|
78
src/kxos-typeflags-util/src/bool.rs
Normal file
78
src/kxos-typeflags-util/src/bool.rs
Normal file
@ -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 {}
|
29
src/kxos-typeflags-util/src/if_.rs
Normal file
29
src/kxos-typeflags-util/src/if_.rs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
15
src/kxos-typeflags-util/src/lib.rs
Normal file
15
src/kxos-typeflags-util/src/lib.rs
Normal file
@ -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;
|
28
src/kxos-typeflags-util/src/same.rs
Normal file
28
src/kxos-typeflags-util/src/same.rs
Normal file
@ -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;
|
107
src/kxos-typeflags-util/src/set.rs
Normal file
107
src/kxos-typeflags-util/src/set.rs
Normal file
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
15
src/kxos-typeflags/Cargo.toml
Normal file
15
src/kxos-typeflags/Cargo.toml
Normal file
@ -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"
|
163
src/kxos-typeflags/src/flag_set.rs
Normal file
163
src/kxos-typeflags/src/flag_set.rs
Normal file
@ -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
|
||||||
|
}
|
63
src/kxos-typeflags/src/lib.rs
Normal file
63
src/kxos-typeflags/src/lib.rs
Normal file
@ -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()
|
||||||
|
}
|
139
src/kxos-typeflags/src/type_flag.rs
Normal file
139
src/kxos-typeflags/src/type_flag.rs
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
src/kxos-typeflags/src/util.rs
Normal file
95
src/kxos-typeflags/src/util.rs
Normal file
@ -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…
x
Reference in New Issue
Block a user