1#![allow(dead_code)]
8
9extern crate proc_macro;
10extern crate regex;
11
12use proc_macro::{TokenTree, TokenStream, LexError};
13use std::fs::File;
14use std::path::Path;
15use std::io::Read;
16use regex::Regex;
17use std::env::VarError;
18
19#[derive(Debug)]
20enum Error {
21 Unknown(String),
22 StructNameNotFound,
23 Syntax(LexError),
24 InvalidArguments(String),
25 RootDirNotFound(VarError),
26 IOError {
27 file: String,
28 error: std::io::Error,
29 },
30 Multiple(Vec<Error>),
31}
32
33impl ToString for Error {
34 fn to_string(&self) -> String {
35 format!("{:?}", self)
36 }
37}
38
39impl From<LexError> for Error {
40 fn from(error: LexError) -> Self {
41 Error::Syntax(error)
42 }
43}
44
45impl From<VarError> for Error {
46 fn from(error: VarError) -> Self {
47 Error::RootDirNotFound(error)
48 }
49}
50
51impl From<Error> for TokenStream {
52 fn from(error: Error) -> Self {
53 format!("compile_error!(r####\"{:?}\"####);", error).parse().unwrap()
54 }
55}
56
57#[derive(Clone, Debug)]
58struct Field {
59 name: String,
60 type_name: String,
61}
62
63#[derive(Clone, Debug)]
64struct Struct {
65 pub name: String,
66 pub fields: Vec<Field>,
67}
68
69fn parse_field<I>(iter: &mut I) -> Option<Field>
70where
71 I : Iterator<Item = TokenTree> + Clone,
72{
73 let field_name = iter.take_while(|v| match v {
74 TokenTree::Punct(punct) => punct.to_string() != ":",
75 _ => true,
76 }).last().map(|v| match v {
77 TokenTree::Ident(ident) => Some(ident.to_string()),
78 _ => None,
79 })??;
80
81 let type_name = iter.take_while(|v| match v {
82 TokenTree::Punct(punct) => punct.to_string() != ",",
83 _ => true,
84 }).map(|v| v.to_string()).collect::<Vec<_>>().join("");
85
86 if type_name != "" {
87 Some(Field {
88 name: field_name,
89 type_name: type_name,
90 })
91 } else {
92 None
93 }
94}
95
96fn parse_struct<I>(iter: &mut I) -> Result<Struct, Error>
97where
98 I : Iterator<Item = TokenTree> + Clone,
99{
100 let name = match iter.skip_while(|item| {
101 match item {
102 TokenTree::Ident(ident) => ident.to_string() != "struct",
103 _ => true
104 }
105 }).skip(1).next().ok_or(Error::StructNameNotFound)? {
106 TokenTree::Ident(ident) => Ok(ident.to_string()),
107 _ => Err(Error::StructNameNotFound)
108 }?;
109
110 let fields = iter.last().map(|group| {
111 match group {
112 TokenTree::Group(group) => Some(group.stream().into_iter()),
113 _ => None,
114 }
115 }).flatten().map(|mut iter| {
116 let mut fields: Vec<Field> = Default::default();
117
118 while let Some(field) = parse_field(&mut iter) {
119 fields.push(field)
120 }
121
122 fields
123 }).unwrap_or_else(|| Default::default());
124
125 Ok(Struct { name, fields })
126}
127
128fn uniforms_impl(tokens: TokenStream) -> Result<TokenStream, Error> {
129 let parsed = parse_struct(&mut tokens.into_iter())?;
130 let source = format!(
131 r####"impl webgl_rc::uniforms::Uniforms for {struct_name} {{
132 fn uniforms(&self) -> Vec<webgl_rc::uniforms::Field> {{
133 use webgl_rc::uniforms::IntoUniform;
134 vec![
135 {content}
136 ]
137 }}
138 }}"####,
139 struct_name = parsed.name,
140 content = &parsed.fields.iter().map(|field| {
141 format!(
142 r###"webgl_rc::uniforms::Field {{ name: r#"u_{name}"#, value: self.{name}.into_uniform() }},"###,
143 name = field.name,
144 )
145 }).collect::<Vec<_>>().join("")
146 );
147 Ok(source.parse()?)
148}
149
150#[proc_macro_derive(Uniforms)]
151pub fn uniforms(tokens: TokenStream) -> TokenStream {
152 uniforms_impl(tokens).unwrap_or_else(|error| error.into())
153}
154
155fn attributes_impl(prefix: &str, tokens: TokenStream) -> Result<TokenStream, Error> {
156 let parsed = parse_struct(&mut tokens.into_iter())?;
157 let source = format!(
158 r####"
159 impl webgl_rc::data_buffer::Item for {struct_name} {{
160 fn layout() -> Vec<webgl_rc::data_buffer::Layout> {{
161 use webgl_rc::types::TypeMark;
162 vec![
163 {layout_items}
164 ]
165 }}
166 }}
167 impl webgl_rc::data_buffer::Writable for {struct_name} {{
168 fn write(&self, output: &mut Vec<f32>) {{
169 use webgl_rc::data_buffer::Writable;
170 {write_items}
171 }}
172 fn stride() -> usize {{
173 use webgl_rc::data_buffer::Writable;
174 {stride_items}
175 }}
176 }}
177 "####,
178 struct_name = parsed.name,
179 layout_items = &parsed.fields.iter().map(|field| {
180 format!(
181 r###"webgl_rc::data_buffer::Layout {{ name: r#"{prefix}_{name}"#, data_type: <{type_name} as TypeMark>::data_type() }},"###,
182 prefix = prefix,
183 name = field.name,
184 type_name = field.type_name,
185 )
186 }).collect::<Vec<_>>().join(""),
187 write_items = &parsed.fields.iter().map(|field| {
188 format!(
189 r###"self.{name}.write(output);"###,
190 name = field.name,
191 )
192 }).collect::<Vec<_>>().join(""),
193 stride_items = &parsed.fields.iter().map(|field| {
194 format!(
195 r###"<{type_name} as Writable>::stride()"###,
196 type_name = field.type_name,
197 )
198 }).collect::<Vec<_>>().join(" + "),
199 );
200 source.parse().map_err(|error: LexError| error.into())
201}
202
203#[proc_macro_derive(Attributes)]
204pub fn attributes(tokens: TokenStream) -> TokenStream {
205 attributes_impl("a", tokens).unwrap_or_else(|error| error.into())
206}
207
208#[proc_macro_derive(Instances)]
209pub fn instances(tokens: TokenStream) -> TokenStream {
210 attributes_impl("i", tokens).unwrap_or_else(|error| error.into())
211}
212
213struct Content {
214 content: String,
215 dependencies: Vec<String>,
216}
217
218fn load_glsl_file(root: &Path, file: &Path) -> Result<Content, Error> {
219 let mut handle = File::open(file).map_err(|error| {
220 Error::IOError {
221 file: file.to_str().unwrap().into(),
222 error,
223 }
224 })?;
225 let mut source: String = Default::default();
226 handle.read_to_string(&mut source).map_err(|error| {
227 Error::IOError {
228 file: file.to_str().unwrap().into(),
229 error,
230 }
231 })?;
232
233 let mut dependencies = vec![file.to_str().unwrap().into()];
234 let mut errors = Vec::new();
235
236 let source_with_includes = Regex::new(r#"#include\s*(<.+?>|".+?")"#)
237 .unwrap()
238 .replace_all(&source, &mut |captures: ®ex::Captures<'_>| {
239 let capture = captures.get(1).unwrap().as_str();
240 let file_name = if capture.starts_with("<") {
241 root.join(capture.get(1..(capture.len() - 1)).unwrap())
242 } else {
243 file.parent().unwrap().join(capture.get(1..(capture.len() - 1)).unwrap())
244 };
245
246 match load_glsl_file(root, &file_name) {
247 Ok(content) => {
248 for file in content.dependencies {
249 dependencies.push(file);
250 }
251 content.content
252 }
253 Err(error) => {
254 errors.push(error);
255 format!("#error Failed to include file {:?}\n", file_name)
256 }
257 }
258 });
259
260 if errors.is_empty() {
261 Ok(Content {
262 content: source_with_includes.into(),
263 dependencies,
264 })
265 } else {
266 Err(Error::Multiple(errors))
267 }
268}
269
270fn load_glsl_impl(stream: TokenStream) -> Result<TokenStream, Error> {
271 let tokens = stream.into_iter().collect::<Vec<_>>();
272 return if tokens.is_empty() {
273 Err(Error::InvalidArguments("File name not provided".into()))
274 } else if tokens.len() > 1 {
275 Err(Error::InvalidArguments("Too many arguments".into()))
276 } else {
277 let name = match tokens.first().unwrap() {
278 TokenTree::Literal(value) => {
279 Ok(
280 value.to_string().chars().into_iter()
281 .skip_while(|c| *c != '"')
282 .skip(1)
283 .take_while(|c| *c != '"')
284 .map(|c| c.to_string())
285 .collect::<Vec<_>>()
286 .join("")
287 )
288 },
289 other => Err(Error::Unknown(format!("File name should be a string but {:?} provided", other)))
290 }?;
291
292 let root = Path::new(
293 &std::env::var("CARGO_MANIFEST_DIR")?
294 ).join("glsl");
295
296 let content = load_glsl_file(root.as_path(), root.join(name).as_path())?;
297
298 Ok(format!(
299 r#####"{{ {dependencies}; r####"{content}"#### }}"#####,
300 dependencies = content.dependencies.into_iter().map(|file| {
301 format!(r##"const _: &[u8] = include_bytes!(r#"{file}"#);"##, file = file)
302 }).collect::<Vec<_>>().join(""),
303 content = content.content
304 ).parse()?)
305 }
306}
307
308#[proc_macro]
314pub fn load_glsl(tokens: TokenStream) -> TokenStream {
315 load_glsl_impl(tokens).unwrap_or_else(|error| error.into())
316}