You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
7.2 KiB
Rust

use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, TokenStream, TokenTree};
use crate::parser::{Component, Name, Param};
use crate::TtIter;
impl Component {
pub(crate) fn compile(self) -> impl TtIter {
// wrap into `<Name as $crate::view::NewView<_>>::into_view()`
self.name
.clone()
.into_new_view()
.chain(syms::parse("::into_view"))
.chain([TokenTree::from(Group::new(
Delimiter::Parenthesis,
TokenStream::from_iter(self.compile_inner()),
))])
}
pub(crate) fn compile_inner(self) -> impl TtIter {
let span = self.name.span();
self.name
.clone()
.into_constructor(self.children.is_some())
.chain([Group::new(
Delimiter::Parenthesis,
TokenStream::from_iter(
syms::id_new_call(span)
.chain([
Punct::new(',', Spacing::Alone).into(),
Self::compile_params(self.name, self.params),
])
.chain(self.children.into_iter().flat_map(|children| {
syms::parse(", ::std::vec::Vec::<_>::from").chain([Group::new(
Delimiter::Parenthesis,
Self::compile_children(children).into(),
)
.into()])
})),
),
)
.into()])
}
/// `Name(key1: val1)` ->
///
/// ```
/// {
/// let mut __uwui_params = <Name as ::uwui::view::NewView>::Params::default();
/// __uwui_params.key1 = ::core::convert::Into::<_>::into(val1);
/// __uwui_params
/// }
/// ```
fn compile_params(name: Name, params: Option<Vec<Param>>) -> TokenTree {
let span = name.span();
Group::new(
Delimiter::Brace,
TokenStream::from_iter(
[
TokenTree::Ident(Ident::new("let", span)),
Ident::new("mut", span).into(),
Ident::new("__uwui_params", span).into(),
Punct::new('=', Spacing::Alone).into(),
]
.into_iter()
.chain(name.into_params_type())
.chain([
Punct::new(':', Spacing::Joint).into(),
Punct::new(':', Spacing::Alone).into(),
Ident::new("default", span).into(),
Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
Punct::new(';', Spacing::Alone).into(),
])
.chain(
params
.unwrap_or(Vec::new())
.into_iter()
.flat_map(|param| param.compile(Ident::new("__uwui_params", span))),
)
.chain([Ident::new("__uwui_params", span).into()]),
),
)
.into()
}
fn compile_children(children: Vec<Component>) -> TokenTree {
Group::new(
Delimiter::Bracket,
TokenStream::from_iter(children.into_iter().flat_map(|child| {
child
.compile()
.chain([Punct::new(',', Spacing::Alone).into()])
})),
)
.into()
}
}
impl Name {
/// `Name -> <Name as $crate::view::NewView<_>>::new` (!group)
/// `Name -> <Name as $crate::view::NewViewGroup<_>>::new_group` (group)
fn into_constructor(self, group: bool) -> impl TtIter {
let span = self.span();
let trait_name = if group { "NewViewGroup" } else { "NewView" };
let fn_name = if group { "new_group" } else { "new" };
[TokenTree::Punct(Punct::new('<', Spacing::Alone))]
.into_iter()
.chain(self.0.into_iter())
.chain([Ident::new("as", span).into()])
.chain(syms::absolute_path(span, ["view", trait_name].into_iter()))
.chain([
Punct::new('<', Spacing::Alone).into(),
Ident::new("_", span).into(),
Punct::new('>', Spacing::Joint).into(),
Punct::new('>', Spacing::Joint).into(),
Punct::new(':', Spacing::Joint).into(),
Punct::new(':', Spacing::Alone).into(),
Ident::new(fn_name, span).into(),
])
}
/// `Name -> <Name as $crate::view::NewView::<_>>::Params`
fn into_params_type(self) -> impl TtIter {
let span = self.span();
self.into_new_view().chain([
Punct::new(':', Spacing::Joint).into(),
Punct::new(':', Spacing::Alone).into(),
Ident::new("Params", span).into(),
])
}
/// `Name -> <Name as $crate::view::NewView<_>>`
fn into_new_view(self) -> impl TtIter {
let span = self.span();
[Punct::new('<', Spacing::Alone).into()]
.into_iter()
.chain(self.0.into_iter())
.chain([Ident::new("as", span).into()])
.chain(syms::new_view(span))
.chain([Punct::new('>', Spacing::Joint).into()])
}
}
impl Param {
/// `name: expr -> ident.name = ::core::convert::Into::<_>::into(expr);`
fn compile(self, ident: Ident) -> impl TtIter {
let (name, colon, expr) = (self.0, self.1, self.2);
let mut eq = Punct::new('=', Spacing::Alone);
eq.set_span(colon.span());
[
ident.into(),
Punct::new('.', Spacing::Alone).into(),
name,
eq.into(),
]
.into_iter()
.chain(syms::parse("::core::convert::Into::<_>::into"))
.chain([
Group::new(
Delimiter::Parenthesis,
TokenStream::from_iter(expr.0.into_iter()),
)
.into(),
Punct::new(';', Spacing::Alone).into(),
])
}
}
/// Helpers for static identifiers within our API
mod syms {
use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream};
use std::str::FromStr;
use crate::TtIter;
/// `$crate::view::Id::new()`
pub(super) fn id_new_call(span: Span) -> impl TtIter {
absolute_path(span, ["view", "Id", "new"].into_iter()).chain([Group::new(
Delimiter::Parenthesis,
TokenStream::new(),
)
.into()])
}
/// `$crate::view::NewView::<_>`
pub(super) fn new_view(span: Span) -> impl TtIter {
absolute_path(span, ["view", "NewView"].into_iter()).chain(parse("::<_>"))
}
pub(super) fn absolute_path<'a>(
span: Span,
segments: impl Iterator<Item = &'a str> + 'a,
) -> impl TtIter + 'a {
[Ident::new("crate", span).into()]
.into_iter()
.chain(segments.flat_map(move |name| {
[
Punct::new(':', Spacing::Joint).into(),
Punct::new(':', Spacing::Alone).into(),
Ident::new(name, span).into(),
]
.into_iter()
}))
}
pub(super) fn parse(code: &str) -> impl TtIter {
TokenStream::from_str(code).unwrap().into_iter()
}
}