2020-04-08 14:44:48 +10:00
|
|
|
//! Parsing of the source
|
|
|
|
|
|
|
|
extern crate proc_macro;
|
|
|
|
|
2020-04-15 05:10:26 +10:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
2020-04-08 14:44:48 +10:00
|
|
|
use syn::{
|
2020-04-11 09:27:21 +10:00
|
|
|
Expr, ExprLit, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, ItemEnum, ItemStruct, Lit,
|
|
|
|
PathArguments, TypeArray, TypePath,
|
2020-04-08 14:44:48 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
/// A scalar that can be represented in a packed data structure.
|
|
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
|
|
pub enum GpuScalar {
|
2020-04-19 10:04:19 +10:00
|
|
|
F16,
|
|
|
|
F32,
|
2020-04-08 14:44:48 +10:00
|
|
|
I8,
|
|
|
|
I16,
|
|
|
|
I32,
|
|
|
|
U8,
|
|
|
|
U16,
|
|
|
|
U32,
|
2021-03-03 04:57:49 +11:00
|
|
|
TagFlags,
|
2020-04-08 14:44:48 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
/// An algebraic datatype.
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum GpuType {
|
|
|
|
Scalar(GpuScalar),
|
|
|
|
Vector(GpuScalar, usize),
|
|
|
|
/// Used mostly for the body of enum variants.
|
|
|
|
InlineStruct(String),
|
|
|
|
Ref(Box<GpuType>),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct GpuEnum {
|
|
|
|
pub name: String,
|
|
|
|
pub variants: Vec<(String, Vec<GpuType>)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum GpuTypeDef {
|
|
|
|
Struct(String, Vec<(String, GpuType)>),
|
|
|
|
Enum(GpuEnum),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct GpuModule {
|
|
|
|
pub name: String,
|
2020-04-15 05:10:26 +10:00
|
|
|
pub attrs: HashSet<String>,
|
2020-04-08 14:44:48 +10:00
|
|
|
pub defs: Vec<GpuTypeDef>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GpuScalar {
|
|
|
|
fn from_syn(ty: &syn::Type) -> Option<Self> {
|
|
|
|
ty_as_single_ident(ty).and_then(|ident| match ident.as_str() {
|
|
|
|
"f32" => Some(GpuScalar::F32),
|
2020-04-19 10:04:19 +10:00
|
|
|
"f16" => Some(GpuScalar::F16),
|
2020-04-08 14:44:48 +10:00
|
|
|
"i8" => Some(GpuScalar::I8),
|
|
|
|
"i16" => Some(GpuScalar::I16),
|
|
|
|
"i32" => Some(GpuScalar::I32),
|
|
|
|
"u8" => Some(GpuScalar::U8),
|
|
|
|
"u16" => Some(GpuScalar::U16),
|
|
|
|
"u32" => Some(GpuScalar::U32),
|
2021-03-03 04:57:49 +11:00
|
|
|
"TagFlags" => Some(GpuScalar::TagFlags),
|
2020-04-08 14:44:48 +10:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
}
|
2020-04-11 09:27:21 +10:00
|
|
|
|
|
|
|
/// Size of scalar type.
|
|
|
|
///
|
|
|
|
/// This is arguably a concern at the layout level, not syntax, but it's here because
|
|
|
|
/// it's not likely to be variable, so reduces the total number of types.
|
|
|
|
pub fn size(self) -> usize {
|
|
|
|
match self {
|
|
|
|
GpuScalar::F32 | GpuScalar::I32 | GpuScalar::U32 => 4,
|
|
|
|
GpuScalar::I8 | GpuScalar::U8 => 1,
|
2020-04-19 10:04:19 +10:00
|
|
|
GpuScalar::F16 | GpuScalar::I16 | GpuScalar::U16 => 2,
|
2021-03-03 04:57:49 +11:00
|
|
|
GpuScalar::TagFlags => 0,
|
2020-04-11 09:27:21 +10:00
|
|
|
}
|
|
|
|
}
|
2020-04-08 14:44:48 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GpuType {
|
|
|
|
fn from_syn(ty: &syn::Type) -> Result<Self, String> {
|
|
|
|
//println!("gputype {:#?}", ty);
|
|
|
|
if let Some(scalar) = GpuScalar::from_syn(ty) {
|
|
|
|
return Ok(GpuType::Scalar(scalar));
|
|
|
|
}
|
|
|
|
if let Some(name) = ty_as_single_ident(ty) {
|
|
|
|
// Note: we're not doing any validation here.
|
|
|
|
return Ok(GpuType::InlineStruct(name));
|
|
|
|
}
|
|
|
|
match ty {
|
|
|
|
syn::Type::Path(TypePath {
|
|
|
|
path: syn::Path { segments, .. },
|
|
|
|
..
|
|
|
|
}) => {
|
|
|
|
if segments.len() == 1 {
|
|
|
|
let seg = &segments[0];
|
|
|
|
if seg.ident == "Ref" {
|
|
|
|
if let PathArguments::AngleBracketed(args) = &seg.arguments {
|
|
|
|
if args.args.len() == 1 {
|
|
|
|
if let GenericArgument::Type(inner) = &args.args[0] {
|
|
|
|
let inner_ty = GpuType::from_syn(inner)?;
|
|
|
|
return Ok(GpuType::Ref(Box::new(inner_ty)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err("unknown path case".into())
|
|
|
|
}
|
|
|
|
syn::Type::Array(TypeArray { elem, len, .. }) => {
|
|
|
|
if let Some(elem) = GpuScalar::from_syn(&elem) {
|
|
|
|
if let Some(len) = expr_int_lit(len) {
|
|
|
|
// maybe sanity-check length here
|
|
|
|
Ok(GpuType::Vector(elem, len))
|
|
|
|
} else {
|
|
|
|
Err("can't deal with variable length scalar arrays".into())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("can't deal with non-scalar arrays".into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err("unknown type".into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GpuTypeDef {
|
|
|
|
fn from_syn(item: &syn::Item) -> Result<Self, String> {
|
|
|
|
match item {
|
|
|
|
syn::Item::Struct(ItemStruct {
|
|
|
|
ident,
|
|
|
|
fields: Fields::Named(FieldsNamed { named, .. }),
|
|
|
|
..
|
|
|
|
}) => {
|
|
|
|
let mut fields = Vec::new();
|
|
|
|
for field in named {
|
|
|
|
let field_ty = GpuType::from_syn(&field.ty)?;
|
|
|
|
let field_name = field.ident.as_ref().ok_or("need name".to_string())?;
|
|
|
|
fields.push((field_name.to_string(), field_ty));
|
|
|
|
}
|
|
|
|
Ok(GpuTypeDef::Struct(ident.to_string(), fields))
|
|
|
|
}
|
|
|
|
syn::Item::Enum(ItemEnum {
|
|
|
|
ident, variants, ..
|
|
|
|
}) => {
|
|
|
|
let mut v = Vec::new();
|
|
|
|
for variant in variants {
|
|
|
|
let vname = variant.ident.to_string();
|
|
|
|
let mut fields = Vec::new();
|
|
|
|
if let Fields::Unnamed(FieldsUnnamed { unnamed, .. }) = &variant.fields {
|
|
|
|
for field in unnamed {
|
|
|
|
fields.push(GpuType::from_syn(&field.ty)?);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v.push((vname, fields));
|
|
|
|
}
|
|
|
|
let en = GpuEnum {
|
|
|
|
name: ident.to_string(),
|
|
|
|
variants: v,
|
|
|
|
};
|
|
|
|
Ok(GpuTypeDef::Enum(en))
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
eprintln!("{:#?}", item);
|
|
|
|
Err("unknown item".into())
|
|
|
|
}
|
|
|
|
}
|
2020-04-11 09:27:21 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn name(&self) -> &str {
|
|
|
|
match self {
|
|
|
|
GpuTypeDef::Struct(name, _) => name,
|
|
|
|
GpuTypeDef::Enum(en) => &en.name,
|
|
|
|
}
|
|
|
|
}
|
2020-04-08 14:44:48 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GpuModule {
|
|
|
|
pub fn from_syn(module: &syn::ItemMod) -> Result<Self, String> {
|
|
|
|
let name = module.ident.to_string();
|
2020-04-15 05:10:26 +10:00
|
|
|
let mut attrs = HashSet::new();
|
|
|
|
for attr in &module.attrs {
|
|
|
|
if let Some(id) = path_as_single_ident(&attr.path) {
|
|
|
|
attrs.insert(id.to_owned());
|
|
|
|
}
|
|
|
|
}
|
2020-04-08 14:44:48 +10:00
|
|
|
let mut defs = Vec::new();
|
|
|
|
if let Some((_brace, items)) = &module.content {
|
|
|
|
for item in items {
|
|
|
|
let def = GpuTypeDef::from_syn(item)?;
|
|
|
|
defs.push(def);
|
|
|
|
}
|
|
|
|
}
|
2020-04-15 05:10:26 +10:00
|
|
|
Ok(GpuModule { name, attrs, defs })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn path_as_single_ident(path: &syn::Path) -> Option<String> {
|
|
|
|
if path.segments.len() == 1 {
|
|
|
|
let seg = &path.segments[0];
|
|
|
|
if seg.arguments == PathArguments::None {
|
|
|
|
return Some(seg.ident.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ty_as_single_ident(ty: &syn::Type) -> Option<String> {
|
|
|
|
if let syn::Type::Path(TypePath { path, .. }) = ty {
|
|
|
|
path_as_single_ident(path)
|
|
|
|
} else {
|
|
|
|
None
|
2020-04-08 14:44:48 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn expr_int_lit(e: &Expr) -> Option<usize> {
|
|
|
|
if let Expr::Lit(ExprLit {
|
|
|
|
lit: Lit::Int(lit_int),
|
|
|
|
..
|
|
|
|
}) = e
|
|
|
|
{
|
|
|
|
lit_int.base10_parse().ok()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|