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.

389 lines
11 KiB
Rust

use crate::TtIter;
use proc_macro::{
Delimiter, Diagnostic, Group, Ident, Level, Spacing, Span, TokenStream, TokenTree,
};
use std::{fmt, iter::Peekable};
/// A single component definition, including children.
pub(crate) struct Component {
/// Event listeners attached to this component.
pub(crate) listeners: Vec<Listener>,
/// The type name of this component (may be namespaced).
pub(crate) name: Name,
/// The parameter list as a dictionary, if `name` is followed by parentheses.
pub(crate) params: Option<Vec<Param>>,
/// All child component definitions.
pub(crate) children: Option<Vec<Component>>,
}
#[derive(Clone)]
pub(crate) struct Name(pub(crate) Vec<TokenTree>);
pub(crate) struct Param(pub(crate) TokenTree, pub(crate) TokenTree, pub(crate) Expr);
pub(crate) struct Expr(pub(crate) Vec<TokenTree>);
pub(crate) struct Listener {
pub(crate) at: TokenTree,
pub(crate) name: TokenTree,
pub(crate) callback: TokenTree,
}
/// The main syntax tree parser. This implements [`Iterator`], so it will
/// only parse (and consume the input iterator) when consumed itself.
pub(crate) struct Parser<'a, T: TtIter> {
stream: &'a mut Peekable<T>,
}
impl<'a, T: TtIter> From<&'a mut Peekable<T>> for Parser<'a, T> {
fn from(stream: &'a mut Peekable<T>) -> Self {
Self { stream }
}
}
impl<'a, T: TtIter> Iterator for Parser<'a, T> {
type Item = Component;
fn next(&mut self) -> Option<Component> {
self.parse_component()
}
}
impl<'a, T: TtIter> Parser<'a, T> {
/// ```notrust
/// component = *listener name space [ param-list space ] [ child-list space ]
/// param-list = "(" space *( param space "," space ) [ param space ] ")"
/// child-list = "{" space *( component ) "}"
/// ```
fn parse_component(&mut self) -> Option<Component> {
self.stream.peek()?;
let mut listeners = Vec::new();
while let Some(listener) = Listener::parse(self.stream) {
Diagnostic::spanned(
listener.span(),
Level::Error,
"Event listeners aren't implemented yet",
)
.emit();
listeners.push(listener);
}
let name = Name::eat(self.stream)?;
let params = self
.stream
.next_if(is_group(Delimiter::Parenthesis))
.map(|tt| match tt {
TokenTree::Group(group) => group.stream().into_iter().peekable(),
_ => unreachable!("is_group() already matched this"),
})
.map(|mut stream| {
let mut list = Vec::new();
while let Some(param) = Param::parse(&mut stream) {
list.push(param);
stream.next_if(is_punct(','));
}
if let Some(tt) = stream.peek() {
Diagnostic::spanned(tt.span(), Level::Error, "Unexpected token").emit();
}
list
});
let children = self
.stream
.next_if(is_group(Delimiter::Brace))
.map(|tt| match tt {
TokenTree::Group(group) => group.stream().into_iter().peekable(),
_ => unreachable!("is_child_list already matched this"),
})
.map(|mut stream| Parser::from(&mut stream).collect());
Some(Component {
listeners,
name,
params,
children,
})
}
}
impl Name {
/// ```notrust
/// name = [ path-sep space ] ident *( space path-sep space ident )
/// ```
fn eat(stream: &mut Peekable<impl TtIter>) -> Option<Self> {
stream.peek()?;
let mut segments = Vec::new();
// [ path-sep ]
segments.extend(
Self::parse_path_sep(stream)
.into_iter()
.flat_map(|sep| sep.into_iter()),
);
// ident
segments.push(eat_ident(stream)?);
// *( path-sep ident )
while let Some(sep) = Self::parse_path_sep(stream) {
segments.extend(sep.into_iter());
segments.push(eat_ident(stream)?);
}
Some(Name(segments))
}
pub(crate) fn span(&self) -> Span {
let first = self.0.first().unwrap().span();
let last = self.0.last().unwrap().span();
first.join(last).unwrap()
}
/// ```notrust
/// path-sep = "::"
/// ```
fn parse_path_sep(stream: &mut Peekable<impl TtIter>) -> Option<[TokenTree; 2]> {
let first = stream.next_if(is_punct_spaced(':', Spacing::Joint))?;
let second = match stream.next() {
Some(tt) => match &tt {
TokenTree::Punct(punct)
if punct.as_char() == ':' && punct.spacing() == Spacing::Alone =>
{
Some(tt)
}
_ => {
Diagnostic::spanned(tt.span(), Level::Error, "Expected a `:`").emit();
None
}
},
None => {
Diagnostic::new(Level::Error, "Unexpected end of stream").emit();
None
}
}?;
Some([first, second])
}
}
impl Param {
/// ```notrust
/// param = ident space ":" space expr
/// ```
fn parse(stream: &mut Peekable<impl TtIter>) -> Option<Self> {
stream.peek()?;
let ident = eat_ident(stream)?;
let colon = match stream.next() {
Some(tt) => match &tt {
TokenTree::Punct(punct)
if punct.as_char() == ':' && punct.spacing() == Spacing::Alone =>
{
Some(tt)
}
_ => {
Diagnostic::spanned(tt.span(), Level::Error, "Expected a `:`").emit();
None
}
},
None => {
Diagnostic::new(Level::Error, "Unexpected end of stream").emit();
None
}
}?;
let expr = Expr::parse(stream)?;
Some(Param(ident, colon, expr))
}
}
impl Expr {
/// ```notrust
/// expr = <now that's just rust code, stop being a pedantic bitch about it>
/// ```
fn parse(stream: &mut Peekable<impl TtIter>) -> Option<Self> {
let mut tokens = Vec::new();
while let Some(tt) = stream.peek() {
if is_punct(',')(tt) {
break;
} else {
tokens.push(check_ident_recurse(
stream.next().expect("We already peeked this"),
));
}
}
if tokens.is_empty() {
None
} else {
Some(Expr(tokens))
}
}
}
impl Listener {
/// ```notrust
/// listener = "@" ident space "(" space expr space ")" space
/// ```
fn parse(stream: &mut Peekable<impl TtIter>) -> Option<Self> {
let at = stream.next_if(is_punct('@'))?;
let name = eat_ident(stream)?;
let callback = match stream.next_if(is_group(Delimiter::Parenthesis)) {
Some(tt) => tt,
None => {
Diagnostic::spanned(
at.span().join(name.span()).unwrap(),
Level::Error,
"Event listeners require a callback",
)
.emit();
return None;
}
};
Some(Self { at, name, callback })
}
fn span(&self) -> Span {
self.at.span().join(self.callback.span()).unwrap()
}
}
/// ```notrust
/// ident = <any valid rust identifier, excluding those starting with "__uwui">
/// ```
fn eat_ident(stream: &mut Peekable<impl TtIter>) -> Option<TokenTree> {
match stream.next() {
Some(tt) => match &tt {
TokenTree::Ident(i) => {
check_ident(i);
Some(tt)
}
_ => {
Diagnostic::spanned(tt.span(), Level::Error, "Expected an identifier").emit();
None
}
},
None => {
Diagnostic::new(Level::Error, "Unexpected end of stream").emit();
None
}
}
}
fn check_ident_recurse(tt: TokenTree) -> TokenTree {
match tt {
TokenTree::Ident(ref ident) => {
check_ident(ident);
tt
}
TokenTree::Group(group) => {
let mut wrapped = Group::new(
group.delimiter(),
TokenStream::from_iter(group.stream().into_iter().map(check_ident_recurse)),
);
wrapped.set_span(group.span());
TokenTree::from(wrapped)
}
_ => tt,
}
}
fn check_ident(ident: &Ident) {
if ident.to_string().starts_with("__uwui") {
Diagnostic::spanned(
ident.span(),
Level::Error,
"Identifiers starting with `__uwui` are reserved",
)
.emit();
}
}
const fn is_punct_spaced(c: char, s: Spacing) -> impl FnMut(&TokenTree) -> bool {
move |tt| match tt {
TokenTree::Punct(punct) => punct.as_char() == c && punct.spacing() == s,
_ => false,
}
}
fn is_punct(c: char) -> impl FnMut(&TokenTree) -> bool {
move |tt| match tt {
TokenTree::Punct(punct) => punct.as_char() == c,
_ => false,
}
}
fn is_group(delim: Delimiter) -> impl FnMut(&TokenTree) -> bool {
move |tt| match tt {
TokenTree::Group(group) => group.delimiter() == delim,
_ => false,
}
}
///////////// debug stuff ///////////////
impl Component {
fn print(&self, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
let indent = " ".repeat(depth);
f.write_str(&indent)?;
fmt::Debug::fmt(&self.name, f)?;
if let Some(params) = &self.params {
f.write_str("(")?;
let mut first = true;
for param in params {
if first {
first = false;
} else {
f.write_str(", ")?;
}
fmt::Debug::fmt(param, f)?;
}
f.write_str(")")?;
}
f.write_str("\n")?;
if let Some(children) = &self.children {
for child in children {
child.print(f, depth + 1)?;
}
}
Ok(())
}
}
impl fmt::Debug for Component {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("\n")?;
self.print(f, 0)?;
f.write_str("\n")
}
}
impl fmt::Debug for Name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for segment in &self.0 {
f.write_str(&segment.to_string())?;
}
Ok(())
}
}
impl fmt::Debug for Param {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.0.to_string())?;
f.write_str(" => ")?;
fmt::Debug::fmt(&self.2, f)
}
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for tt in &self.0 {
f.write_str(&tt.to_string())?;
}
Ok(())
}
}