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
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()
|
|
}
|
|
}
|