use super::types::{EmissionError, Emittable};
use crate::closure::PartitionCargo;
use crate::token::{
GeneratedDelimiter, GeneratedLiteralForm, GeneratedSpacing, GeneratedToken, GeneratedTree,
SpanHandle,
};
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use std::ffi::CString;
use std::str::FromStr;
pub fn emit(
emittable: &impl Emittable,
spans: &super::Spans,
) -> Result<TokenStream, EmissionError> {
let mut emitted = TokenStream::new();
for cargo in emittable.cargos() {
emitted.extend(emit_cargo(cargo, spans)?);
}
Ok(emitted)
}
fn emit_cargo(cargo: &PartitionCargo, spans: &super::Spans) -> Result<TokenStream, EmissionError> {
cargo
.tokens()
.map_or_else(|| Ok(TokenStream::new()), |tree| emit_tree(tree, spans))
}
pub fn emit_tree(tree: &GeneratedTree, spans: &super::Spans) -> Result<TokenStream, EmissionError> {
let mut sources = tree.source_spans().iter();
let emitted = tree
.tokens()
.iter()
.map(|token| emit_token(token, &mut sources, spans))
.collect::<Result<TokenStream, _>>()?;
if sources.next().is_some() {
return Err(EmissionError::SourceSpanRosterContradiction);
}
Ok(emitted)
}
fn emit_token(
token: &GeneratedToken,
sources: &mut core::slice::Iter<'_, Option<SpanHandle>>,
spans: &super::Spans,
) -> Result<TokenTree, EmissionError> {
let source = *sources
.next()
.ok_or(EmissionError::SourceSpanRosterContradiction)?;
let mut emitted = match token {
GeneratedToken::Word(word) => TokenTree::Ident(Ident::new(word, Span::call_site())),
GeneratedToken::RawIdentifier(name) => {
TokenTree::Ident(Ident::new_raw(name, Span::call_site()))
}
GeneratedToken::Punct { mark, spacing } => {
TokenTree::Punct(Punct::new(*mark, written_spacing(*spacing)))
}
GeneratedToken::Text(text) => TokenTree::Literal(Literal::string(text)),
GeneratedToken::Group { delimiter, tokens } => {
let stream = tokens
.iter()
.map(|nested_token| emit_token(nested_token, sources, spans))
.collect::<Result<_, _>>()?;
TokenTree::Group(Group::new(written_delimiter(*delimiter), stream))
}
GeneratedToken::ByteText(material) => TokenTree::Literal(Literal::byte_string(material)),
GeneratedToken::Number(value) => TokenTree::Literal(Literal::u64_unsuffixed(*value)),
GeneratedToken::Literal(literal) => TokenTree::Literal(emit_literal(literal.form())?),
};
if let Some(handle) = source {
let authored_span = spans
.resolve(handle)
.map_err(EmissionError::SourceSpanUnresolved)?;
emitted.set_span(authored_span);
}
Ok(emitted)
}
fn emit_literal(literal: GeneratedLiteralForm<'_>) -> Result<Literal, EmissionError> {
match literal {
GeneratedLiteralForm::Number(spelling) => {
Literal::from_str(spelling).map_err(|_| EmissionError::NumberRejected {
spelling: spelling.to_owned(),
})
}
GeneratedLiteralForm::Character(character) => Ok(Literal::character(character)),
GeneratedLiteralForm::Byte(byte) => Ok(Literal::byte_character(byte)),
GeneratedLiteralForm::NulTerminatedText(material) => CString::new(material)
.map(|text| Literal::c_string(&text))
.map_err(|_| EmissionError::NulTerminatedTextRejected),
}
}
const fn written_spacing(spacing: GeneratedSpacing) -> Spacing {
match spacing {
GeneratedSpacing::Joint => Spacing::Joint,
GeneratedSpacing::Alone => Spacing::Alone,
}
}
const fn written_delimiter(delimiter: GeneratedDelimiter) -> Delimiter {
match delimiter {
GeneratedDelimiter::Parenthesis => Delimiter::Parenthesis,
GeneratedDelimiter::Brace => Delimiter::Brace,
GeneratedDelimiter::Bracket => Delimiter::Bracket,
GeneratedDelimiter::Bare => Delimiter::None,
}
}