#![allow(clippy::tabs_in_doc_comments)]
extern crate alloc;
extern crate proc_macro;
use crate::trees::null::make_null_group;
use crate::trees::throw_sg_err;
use crate::{
include::{InjectArr, InjectCTT, InjectStr, InjectTT, macro_rule_include},
trees::{
replace::{replace_tree_in_group, replace_tree_in_stream},
result::TreeResult,
search::SearchGroup,
tq,
},
};
use core::slice::IterMut;
use proc_macro::TokenStream;
use proc_macro2::{Delimiter, Group, Span, TokenStream as TokenStream2, TokenTree as TokenTree2};
use quote::{format_ident, quote};
use std::path::Path;
pub(crate) mod trees {
pub mod group;
pub mod null;
pub mod replace;
pub mod search;
#[macro_use]
pub mod result;
#[allow(clippy::single_component_path_imports)]
pub(crate) use tq;
#[macro_use]
pub mod sq_err;
#[allow(clippy::single_component_path_imports)]
pub(crate) use throw_sg_err;
pub mod loader;
}
pub(crate) mod exprs {
pub mod literal;
}
pub(crate) mod include;
pub(crate) struct PointTrack<'tk> {
prefix_token: &'tk mut TokenTree2,
name_token: &'tk mut TokenTree2,
data_token: &'tk mut TokenTree2,
appends_files: usize,
globalposnum: usize,
}
impl<'tk> PointTrack<'tk> {
#[inline]
pub const fn new(
globalposnum: usize,
prefix_token: &'tk mut TokenTree2,
name_token: &'tk mut TokenTree2,
data_token: &'tk mut TokenTree2,
) -> Self {
Self {
prefix_token,
name_token,
data_token,
appends_files: 0,
globalposnum,
}
}
#[inline]
pub fn prefix_span(&self) -> Span {
self.prefix_token.span()
}
#[inline]
pub fn name_span(&self) -> Span {
self.name_token.span()
}
#[inline]
pub fn data_span(&self) -> Span {
self.data_token.span()
}
#[inline]
pub const fn is_rewritten(&self) -> bool {
self.appends_files > 0
}
pub fn into_token_tree2(self) -> Option<(usize, TokenTree2)> {
match self.appends_files {
0 => None,
appends_files => {
let data_span = self.data_span();
Some((
appends_files,
std::mem::replace(self.data_token, make_null_group(data_span)),
))
}
}
}
pub fn append_track_file(&mut self, path: &Path) {
let name_const = format_ident!(
"_TRACKER_FILE_NUM_{}",
self.globalposnum + self.appends_files
);
let path = format!("../{}", path.display());
let ts2 = TokenStream2::from_iter(quote! {
const #name_const: &'static [u8] = include_bytes!(#path) as &[_];
});
self.append_track_files_ts(ts2)
}
pub fn append_track_files_ts(&mut self, ts2: TokenStream2) {
let data_span = self.data_span();
let is_initappendfiles = self.appends_files == 0;
self.appends_files += 1;
let mut ngroup = Group::new(Delimiter::None, ts2);
ngroup.set_span(data_span);
if is_initappendfiles {
*self.data_token = ngroup.into();
} else {
match &mut self.data_token {
TokenTree2::Group(group) => {
let mut new_group: Vec<TokenTree2> = group.stream().into_iter().collect();
new_group.push(ngroup.into());
let mut ngroup =
Group::new(Delimiter::None, TokenStream2::from_iter(new_group));
ngroup.set_span(data_span);
*self.data_token = ngroup.into();
}
_ => panic!(
"Undefined behavior reported in `PointTrack`, someone redefined `TokenTree2`, expected `TokenTree2::Group`"
),
}
}
}
}
impl<'tk> Drop for PointTrack<'tk> {
fn drop(&mut self) {
if !self.is_rewritten() {
let data_span = self.data_span();
*self.data_token = make_null_group(data_span);
}
*self.prefix_token = make_null_group(self.prefix_token.span());
*self.name_token = make_null_group(self.prefix_token.span());
}
}
fn autoinject_tt_in_group<'tk, 'gpsn>(
globalposnum: &'gpsn mut usize,
mut iter: IterMut<'tk, TokenTree2>,
point_track_file: &'_ mut Option<PointTrack<'tk>>,
) -> SearchGroup {
'sbegin: while let Some(m_punct) = iter.next() {
match m_punct {
#[cfg(feature = "escape_symbol")]
TokenTree2::Punct(punct) if punct.as_char() == '-' => {
let mut is_allow_skip_atree = false;
if let Some(TokenTree2::Punct(punct)) = iter.next() {
if punct.as_char() == '#' {
is_allow_skip_atree = true
}
}
if is_allow_skip_atree {
*m_punct = make_null_group(m_punct.span());
}
continue 'sbegin;
}
TokenTree2::Punct(punct) if punct.as_char() == '#' => {
if let Some(m_ident) = iter.next() {
if let TokenTree2::Ident(ident) = m_ident {
#[allow(clippy::type_complexity)]
let macro_fn = match &*ident {
ident if ident == "AS_IS" => {
if let Some(m_punct2) = iter.next() {
if let TokenTree2::Punct(punct2) = m_punct2 {
if punct2.as_char() == ':' {
*m_ident = make_null_group(m_ident.span());
*m_punct = make_null_group(m_punct.span());
*m_punct2 = make_null_group(m_punct2.span());
return SearchGroup::Break;
}
}
}
throw_sg_err! {
return [ident.span()]: "`:` was expected."
}
}
ident if ident == "POINT_TRACKER_FILES" => {
if let Some(m_punct2) = iter.next() {
if let TokenTree2::Punct(punct2) = m_punct2 {
if punct2.as_char() == ':' {
*point_track_file = Some(PointTrack::new(
*globalposnum,
m_punct,
m_ident,
m_punct2,
));
continue 'sbegin;
}
}
}
throw_sg_err! {
return [ident.span()]: "`:` was expected."
}
}
ident if ident == "tt" => {
macro_rule_include::<InjectTT>
as fn(
&Group,
Option<&mut PointTrack<'tk>>,
) -> TreeResult<TokenTree2>
}
ident if ident == "ctt" => macro_rule_include::<InjectCTT> as _,
ident if ident == "str" => macro_rule_include::<InjectStr> as _,
ident if ident == "arr" || ident == "array" => {
macro_rule_include::<InjectArr> as _
}
ident if ident == "break" => {
if let Some(m_punct2) = iter.next() {
if let TokenTree2::Punct(punct2) = m_punct2 {
if punct2.as_char() == ';' {
*m_ident = make_null_group(m_ident.span());
*m_punct = make_null_group(m_punct.span());
*m_punct2 = make_null_group(m_punct2.span());
return SearchGroup::Break;
}
}
}
throw_sg_err! {
return [ident.span()]: "`;` was expected."
}
}
_ => throw_sg_err! {
return [ident.span()]: "Undefined action to include data in macro or change its behavior, expected macro data type: `tt`, `ctt`, `arr`, `str`, or marker: `#AS_IS:`, `#POINT_TRACKER_FILES:`, or stop parsing macro via `#break;`."
},
};
if let Some(m_group) = iter.next() {
if let TokenTree2::Group(group) = m_group {
let result = tq!(macro_fn(group, point_track_file.as_mut()));
*m_ident = make_null_group(m_ident.span());
*m_punct = make_null_group(m_punct.span());
*m_group = result;
continue 'sbegin;
}
}
throw_sg_err! {
return [ident.span()]: "After this input, the group `()`, `[]`, `{}` is expected."
}
}
}
}
TokenTree2::Group(group) => match replace_tree_in_group(group, |iter| {
let mut prefixgroup;
let mut namegroup;
let mut datagroup;
#[allow(clippy::manual_map)] let mut ptf = match point_track_file {
Some(point_track_file) => Some({
prefixgroup = make_null_group(point_track_file.prefix_span());
namegroup = make_null_group(point_track_file.name_span());
datagroup = make_null_group(point_track_file.data_span());
PointTrack::new(
*globalposnum,
&mut prefixgroup,
&mut namegroup,
&mut datagroup,
)
}),
None => None,
};
let result = autoinject_tt_in_group(globalposnum, iter, &mut ptf);
if let Some(ptf) = ptf {
if ptf.is_rewritten() {
if let Some(point_track_file) = point_track_file {
match ptf.into_token_tree2() {
Some((appends_files, TokenTree2::Group(group))) => {
*globalposnum += appends_files;
point_track_file.append_track_files_ts(group.stream());
}
_ => panic!(
"Undefined behavior reported in `PointTrack`, someone redefined `TokenTree2`, expected `TokenTree2::Group`"
),
}
}
}
}
result
}) {
SearchGroup::Break => continue 'sbegin,
result @ SearchGroup::Error(..) => return result,
},
_ => {}
}
}
SearchGroup::Break
}
#[proc_macro]
pub fn inject(input: TokenStream) -> TokenStream {
let mut tt: TokenStream2 = input.into();
match replace_tree_in_stream(&mut tt, |iter| {
autoinject_tt_in_group(&mut 0, iter, &mut None)
}) {
SearchGroup::Error(e) => e.into(),
SearchGroup::Break => tt.into(),
}
}