extern crate alloc;
extern crate proc_macro;
use crate::points::file_dep_tracker::FileDepTracker;
use crate::trees::null::make_null_group;
use crate::trees::sq_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::{Group, TokenStream as TokenStream2, TokenTree as TokenTree2};
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]
#[path = "sq_err.rs"]
mod _sq_err;
#[allow(clippy::single_component_path_imports)]
pub(crate) use __sq_err_format;
#[allow(clippy::single_component_path_imports)]
pub(crate) use sq_err;
pub mod loader;
}
pub(crate) mod exprs {
pub mod literal;
}
pub(crate) mod include;
pub(crate) mod points {
pub mod file_dep_tracker;
}
fn autoinject_tt_in_group<'tk, 'gpsn>(
globalposnum: &'gpsn mut usize,
mut iter: IterMut<'tk, TokenTree2>,
tracker: &'_ mut Option<FileDepTracker<'tk>>,
) -> SearchGroup {
'sbegin: while let Some(mut m_punct) = iter.next() {
'match_m_punct: loop {
match m_punct {
TokenTree2::Punct(punct) if punct.as_char() == '-' => {
let iter_next = match iter.next() {
Some(a) => a,
None => break 'sbegin,
};
if let TokenTree2::Punct(punct) = iter_next
&& punct.as_char() == '#'
{
*m_punct = make_null_group(m_punct.span());
continue 'sbegin;
}
m_punct = iter_next;
continue 'match_m_punct;
}
TokenTree2::Punct(punct) if punct.as_char() == '#' => {
if let Some(m_ident) = iter.next()
&& let TokenTree2::Ident(ident) = m_ident
{
let macro_fn = match &*ident {
ident if ident == "AS_IS" => {
if let Some(m_punct2) = iter.next()
&& let TokenTree2::Punct(punct2) = m_punct2
&& 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;
}
sq_err! {
return [ident.span()]: "`:` was expected."
}
}
ident if ident == "TRACK_FILES" || ident == "POINT_TRACKER_FILES" => {
if let Some(m_punct2) = iter.next()
&& let TokenTree2::Punct(punct2) = m_punct2
&& punct2.as_char() == ':'
{
*tracker = Some(FileDepTracker::new(
*globalposnum,
m_punct,
m_ident,
m_punct2,
));
continue 'sbegin;
}
sq_err! {
return [ident.span()]: "`:` was expected."
}
}
ident if ident == "tt" => {
macro_rule_include::<InjectTT>
as fn(
&Group,
Option<&mut FileDepTracker<'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()
&& let TokenTree2::Punct(punct2) = m_punct2
&& 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;
}
sq_err! {
return [ident.span()]: "`;` was expected."
}
}
_ => sq_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:`, `#TRACK_FILES:`, or stop parsing macro via `#break;`."
},
};
if let Some(m_group) = iter.next()
&& let TokenTree2::Group(group) = m_group
{
let result = tq!(macro_fn(group, tracker.as_mut()));
*m_ident = make_null_group(m_ident.span());
*m_punct = make_null_group(m_punct.span());
*m_group = result;
continue 'sbegin;
}
sq_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 tracker {
Some(tracker) => Some({
prefixgroup = make_null_group(tracker.prefix_span());
namegroup = make_null_group(tracker.name_span());
datagroup = make_null_group(tracker.data_span());
FileDepTracker::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
&& ptf.is_rewritten()
&& let Some(tracker) = tracker
{
match ptf.into_token_tree2() {
Some((appends_files, TokenTree2::Group(group))) => {
*globalposnum += appends_files;
tracker.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,
},
_ => {}
}
break 'match_m_punct;
}
}
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(),
}
}