use std::fmt;
use std::str::FromStr;
use syntax::{ast, ext};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic;
use syntax::{attr, codemap};
use syntax::parse::token;
use syntax::ptr::P;
use syntax::ext::base::ItemDecorator;
#[derive(Copy, PartialEq)]
enum Modifier {
Normalized,
AsFloat,
AsDouble,
}
impl fmt::Debug for Modifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Modifier::Normalized => write!(f, "normalized"),
Modifier::AsFloat => write!(f, "as_float"),
Modifier::AsDouble => write!(f, "as_double"),
}
}
}
impl FromStr for Modifier {
fn from_str(src: &str) -> Option<Modifier> {
match src {
"normalized" => Some(Modifier::Normalized),
"as_float" => Some(Modifier::AsFloat),
"as_double" => Some(Modifier::AsDouble),
_ => None,
}
}
}
fn find_modifier(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
attributes: &[ast::Attribute]) -> Option<Modifier> {
attributes.iter().fold(None, |modifier, attribute| {
match attribute.node.value.node {
ast::MetaWord(ref word) => {
word.get().parse().and_then(|new_modifier| {
attr::mark_used(attribute);
modifier.map_or(Some(new_modifier), |modifier| {
cx.span_warn(span, &format!(
"Extra attribute modifier detected: `#[{:?}]` - \
ignoring in favour of `#[{:?}]`.", new_modifier, modifier
)[]);
None
})
}).or(modifier)
},
_ => modifier,
}
})
}
fn decode_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
ty_ident: &ast::Ident, modifier: Option<Modifier>,
path_root: ast::Ident) -> P<ast::Expr> {
let ty_str = ty_ident.name.as_str();
match ty_str {
"f32" | "f64" => {
let kind = cx.ident_of(match modifier {
None | Some(Modifier::AsFloat) => "Default",
Some(Modifier::AsDouble) => "Precision",
Some(Modifier::Normalized) => {
cx.span_warn(span, &format!(
"Incompatible float modifier attribute: `#[{:?}]`", modifier
)[]);
""
}
});
let size = cx.ident_of(&format!("F{}", ty_str.slice_from(1))[]);
quote_expr!(cx, $path_root::gfx::attrib::Type::Float($path_root::gfx::attrib::FloatSubType::$kind,
$path_root::gfx::attrib::FloatSize::$size))
},
"u8" | "u16" | "u32" | "u64" |
"i8" | "i16" | "i32" | "i64" => {
let sign = cx.ident_of({
if ty_str.starts_with("i") { "Signed" } else { "Unsigned" }
});
let kind = cx.ident_of(match modifier {
None => "Raw",
Some(Modifier::Normalized) => "Normalized",
Some(Modifier::AsFloat) => "AsFloat",
Some(Modifier::AsDouble) => {
cx.span_warn(span, &format!(
"Incompatible int modifier attribute: `#[{:?}]`", modifier
)[]);
""
}
});
let size = cx.ident_of(&format!("U{}", ty_str.slice_from(1))[]);
quote_expr!(cx, $path_root::gfx::attrib::Type::Int($path_root::gfx::attrib::IntSubType::$kind,
$path_root::gfx::attrib::IntSize::$size,
$path_root::gfx::attrib::SignFlag::$sign))
},
ty_str => {
cx.span_err(span, &format!("Unrecognized component type: `{:?}`",
ty_str)[]);
cx.expr_tuple(span, vec![])
},
}
}
fn decode_count_and_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
field: &ast::StructField,
path_root: ast::Ident) -> (P<ast::Expr>, P<ast::Expr>) {
let modifier = find_modifier(cx, span, &field.node.attrs[]);
match field.node.ty.node {
ast::TyPath(ref p, _) => (
cx.expr_lit(span, ast::LitInt(1, ast::UnsuffixedIntLit(ast::Plus))),
decode_type(cx, span, &p.segments[0].identifier, modifier, path_root),
),
ast::TyFixedLengthVec(ref pty, ref expr) => (expr.clone(), match pty.node {
ast::TyPath(ref p, _) => {
decode_type(cx, span, &p.segments[0].identifier, modifier, path_root)
},
_ => {
cx.span_err(span, &format!("Unsupported fixed vector sub-type: \
`{:?}`",pty.node)[]);
cx.expr_tuple(span, vec![])
},
}),
_ => {
cx.span_err(span, &format!("Unsupported attribute type: `{:?}`",
field.node.ty.node)[]);
(cx.expr_tuple(span, vec![]), cx.expr_tuple(span, vec![]))
},
}
}
fn method_body(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
substr: &generic::Substructure,
path_root: ast::Ident) -> P<ast::Expr> {
match *substr.fields {
generic::StaticStruct(ref definition, generic::Named(ref fields)) => {
let attribute_pushes = definition.fields.iter().zip(fields.iter())
.map(|(def, &(ident, _))| {
let struct_ident = substr.type_ident;
let buffer_expr = &substr.nonself_args[1];
let (count_expr, type_expr) = decode_count_and_type(cx, span, def, path_root);
let ident_str = match super::find_name(cx, span, &def.node.attrs[]) {
Some(name) => name,
None => token::get_ident(ident),
};
let ident_str = ident_str.get();
let instance_expr = cx.expr_u8(span, 0); quote_expr!(cx, {
attributes.push($path_root::gfx::Attribute {
name: $ident_str.to_string(),
buffer: $buffer_expr,
format: $path_root::gfx::attrib::Format {
elem_count: $count_expr,
elem_type: $type_expr,
offset: unsafe {
let x: $struct_ident = ::std::mem::uninitialized();
let offset = (&x.$ident as *const _ as usize) -
(&x as *const _ as usize);
::std::mem::forget(x);
offset as $path_root::gfx::attrib::Offset
},
stride: { use std::mem;
mem::size_of::<$struct_ident>() as
$path_root::gfx::attrib::Stride
},
instance_rate: $instance_expr,
}
});
})
}).collect::<Vec<P<ast::Expr>>>();
let capacity = fields.len();
quote_expr!(cx, {
let mut attributes = Vec::with_capacity($capacity);
$attribute_pushes;
attributes
})
},
_ => {
cx.span_err(span, "Unable to implement `gfx::VertexFormat::generate` \
on a non-structure");
cx.expr_tuple(span, vec![])
}
}
}
#[derive(Copy)]
pub struct VertexFormat;
impl ItemDecorator for VertexFormat {
fn expand(&self, context: &mut ext::base::ExtCtxt, span: codemap::Span,
meta_item: &ast::MetaItem, item: &ast::Item,
mut push: Box<FnMut(P<ast::Item>)>) {
let path_root = super::extern_crate_hack(context, span, |i| (*push)(i));
let fixup = |: item| {
(*push)(super::fixup_extern_crate_paths(item, path_root))
};
generic::TraitDef {
span: span,
attributes: Vec::new(),
path: generic::ty::Path {
path: vec![super::EXTERN_CRATE_HACK, "gfx", "VertexFormat"],
lifetime: None,
params: Vec::new(),
global: true,
},
additional_bounds: Vec::new(),
generics: generic::ty::LifetimeBounds::empty(),
methods: vec![
generic::MethodDef {
name: "generate",
generics: generic::ty::LifetimeBounds::empty(),
explicit_self: None,
args: vec![
generic::ty::Literal(generic::ty::Path {
path: vec!["Option"],
lifetime: None,
params: vec![box generic::ty::Self],
global: false,
}),
generic::ty::Literal(generic::ty::Path::new(
vec![super::EXTERN_CRATE_HACK, "gfx", "RawBufferHandle"]
)),
],
ret_ty: generic::ty::Literal(
generic::ty::Path {
path: vec!["Vec"],
lifetime: None,
params: vec![
box generic::ty::Literal(generic::ty::Path::new(
vec![super::EXTERN_CRATE_HACK, "gfx", "Attribute"])),
],
global: false,
},
),
attributes: Vec::new(),
combine_substructure: generic::combine_substructure(
box |c, s, ss| method_body(c, s, ss, path_root)),
},
],
associated_types: Vec::new(),
}.expand(context, meta_item, item, fixup);
}
}