extern crate proc_macro;
use expry::memorypool::*;
use expry::*;
use litrs::StringLit;
use quote::*;
fn first_string(tokens: proc_macro2::TokenStream) -> String {
for i in tokens {
if let Ok(lit) = StringLit::try_from(i) {
return lit.value().to_string();
}
}
String::new()
}
#[proc_macro]
pub fn expry(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let expr = first_string(input);
pool!(scope);
let compile_result = expry_compile_expr(&expr, None, None, &[], &mut scope);
match compile_result {
Ok((bytecode,_)) => {
let lit = proc_macro2::Literal::byte_string(bytecode.get());
quote!{ ::expry::BytecodeRef(#lit) }.into()
},
Err(err) => {
let msg = expry_compile_error_format_short(&err);
compile_error_as_expr(&msg)
},
}
}
struct Type<'a>(&'a ExpryType);
impl<'a> ToTokens for Type<'a> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match &self.0 {
ExpryType::Any => tokens.extend(quote!{ ::expry::ExpryType::Any }),
ExpryType::Null => tokens.extend(quote!{ ::expry::ExpryType::Null }),
ExpryType::Bool => tokens.extend(quote!{ ::expry::ExpryType::Bool }),
ExpryType::Int => tokens.extend(quote!{ ::expry::ExpryType::Int }),
ExpryType::Float => tokens.extend(quote!{ ::expry::ExpryType::Float }),
ExpryType::Double => tokens.extend(quote!{ ::expry::ExpryType::Double }),
ExpryType::String => tokens.extend(quote!{ ::expry::ExpryType::String }),
ExpryType::Object(v) => {
let mut spec = proc_macro2::TokenStream::new();
for (k,(required,v)) in v {
let k = String::from_utf8_lossy(k).to_string();
let v = Type(v);
spec.extend(quote!{ ((#k).as_bytes().to_vec(), (#required,#v)), });
}
tokens.extend(quote!{ ::expry::ExpryType::Object(::std::collections::BTreeMap::from([#spec])) });
},
ExpryType::UniformObject(v) => {
let v = Type(v);
tokens.extend(quote!{ ::expry::ExpryType::UniformObject(::std::boxed::Box::new(#v)) });
},
ExpryType::Array(v) => {
let v = Type(v);
tokens.extend(quote!{ ::expry::ExpryType::Array(::std::boxed::Box::new(#v)) });
},
ExpryType::StaticArray(v) => {
let mut spec = proc_macro2::TokenStream::new();
for v in v {
let v = Type(v);
spec.extend(quote!{ #v, });
}
tokens.extend(quote!{ ::expry::ExpryType::StaticArray(::std::vec::Vec::from([#spec])) });
},
ExpryType::EmptyArray => tokens.extend(quote!{ ::expry::ExpryType::EmptyArray }),
ExpryType::Nullable(v) => {
let v = Type(v);
tokens.extend(quote!{ ::expry::ExpryType::Nullable(::std::boxed::Box::new(#v)) });
},
}
}
}
#[proc_macro]
pub fn expry_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let expr = first_string(input);
pool!(scope);
let types = expry_parse_type(&expr, &mut scope);
match types {
Ok(types) => {
let types = Type(&types);
quote!{ #types }.into()
},
Err(err) => {
let msg = expry_compile_error_format_short(&err);
compile_error_as_expr(&msg)
},
}
}
#[allow(dead_code)]
fn compile_error(string: &str) -> proc_macro::TokenStream {
let expanded = quote!(
compile_error!(#string);
);
proc_macro::TokenStream::from(expanded)
}
fn compile_error_as_expr(string: &str) -> proc_macro::TokenStream {
let expanded = quote!(
compile_error!(#string)
);
proc_macro::TokenStream::from(expanded)
}
#[cfg(nightly_build)]
#[allow(dead_code)]
fn compile_warning(span: proc_macro2::Span, message: &str) {
Diagnostic::spanned(span.unwrap(), proc_macro::Level::Warning, message)
.emit();
}
#[cfg(not(nightly_build))]
#[allow(dead_code)]
fn compile_warning(_span: proc_macro2::Span, _message: &str) {
}
#[cfg(nightly_build)]
#[allow(dead_code)]
fn compile_error_at(span: proc_macro2::Span, message: &str) {
Diagnostic::spanned(span.unwrap(), proc_macro::Level::Error, message)
.emit();
}
#[cfg(not(nightly_build))]
#[allow(dead_code)]
fn compile_error_at(_span: proc_macro2::Span, _message: &str) {
}
#[cfg(test)]
mod tests {
}