mod logger_message;
mod signature;
mod template;
use yog_ids::script_problem;
use yog_script_ids::system::{LOGGER, LOGGER_MESSAGE};
use super::calls::{decl_end, read};
use super::tree::{Tree, flatten, tree_at};
use crate::source::token::{Punct, Tok, Token};
use yog_analysis::Report;
pub(super) fn expand(mut tokens: Vec<Token>, text: &str, report: &mut Report<'_>) -> Vec<Token> {
let mut i = 0;
while i < tokens.len() {
let Some((args, after)) = logger_message_at(&tokens, i) else {
i += 1;
continue;
};
let at = tokens[i].span;
let end = decl_end(&tokens, after);
let sig = signature::signature(&tokens[after..end], text);
let args = signature::args(&args, text);
let logger = enclosing_logger(&tokens, i);
let code = sig
.ok_or_else(|| "`[LoggerMessage]` is for a method".to_string())
.and_then(|s| logger_message::generate(&s, &args, logger.as_deref()));
match code {
Ok(code) => {
let made = read(&code, at, report);
i += made.len();
tokens.splice(i - made.len()..end, made);
}
Err(why) => {
report.error(script_problem::LOGGER_MESSAGE, at, why);
i = end;
}
}
}
tokens
}
fn logger_message_at(tokens: &[Token], i: usize) -> Option<(Vec<Token>, usize)> {
if tokens[i].tok != Tok::Punct(Punct::LBracket) {
return None;
}
let (attr, after) = tree_at(tokens, i);
let Tree::Group { inner, .. } = attr else {
return None;
};
let [Tree::Leaf(word), Tree::Group { inner: args, .. }] = inner.as_slice() else {
return None;
};
if word.tok != Tok::Ident(LOGGER_MESSAGE.to_string()) {
return None;
}
let mut flat = Vec::new();
flatten(args, &mut flat);
Some((flat, after))
}
fn enclosing_logger(tokens: &[Token], i: usize) -> Option<String> {
let mut depth = 0;
let open = (0..i).rev().find(|&k| {
match tokens[k].tok {
Tok::Punct(Punct::RBrace) => depth += 1,
Tok::Punct(Punct::LBrace) if depth == 0 => return true,
Tok::Punct(Punct::LBrace) => depth -= 1,
_ => {}
}
false
})?;
let (body, _) = tree_at(tokens, open);
let Tree::Group { inner, .. } = body else {
return None;
};
let fields = inner.iter().filter_map(|t| match t {
Tree::Leaf(l) => Some(l.clone()),
Tree::Group { .. } => None,
});
let head = (0..open)
.rev()
.take_while(|&k| {
!matches!(
tokens[k].tok,
Tok::Punct(Punct::Semi | Punct::RBrace | Punct::LBrace)
)
})
.map(|k| tokens[k].clone())
.collect::<Vec<_>>()
.into_iter()
.rev();
named_logger(head.chain(fields))
}
fn named_logger(tokens: impl Iterator<Item = Token>) -> Option<String> {
let mut tokens = tokens.skip_while(|t| t.tok != Tok::Ident(LOGGER.to_string()));
tokens.next()?;
tokens
.find_map(|t| match t.tok {
Tok::Ident(n) => Some(n),
Tok::Punct(Punct::Question) => None,
_ => Some(String::new()),
})
.filter(|n| !n.is_empty())
}