use proc_macro2::{Ident, Punct, Spacing};
use syn::{Error, token::{Gt, Div, And, Paren, Brace, Pound}, LitStr, parse::{Parse, ParseStream, ParseBuffer, discouraged::Speculative}, ext::IdentExt, Token, LitInt, LitFloat};
use quote::ToTokens;
use super::*;
fn parse_many<'a, T: Parse>(input: &'a ParseBuffer<'a>) -> syn::Result<Vec<T>> {
let mut v = Vec::new();
loop {
let fork = input.fork();
if let Ok(c) = fork.parse() {
v.push(c);
input.advance_to(&fork);
} else {
break;
}
}
Ok(v)
}
impl Parse for Xml {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut xml = Vec::new();
while !input.is_empty() {
xml.push(input.parse()?);
}
Ok(Self(xml))
}
}
impl Parse for SimpleName {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut segments = Vec::new();
while input.peek(Token![-]) {
segments.push(SimpleNameSeg::Minus(input.parse()?));
}
segments.push(SimpleNameSeg::Ident(Ident::parse_any(input)?));
while input.peek(Token![-]) {
while input.peek(Token![-]) {
segments.push(SimpleNameSeg::Minus(input.parse()?));
}
segments.push(SimpleNameSeg::Ident(Ident::parse_any(input)?));
}
Ok(Self(segments))
}
}
impl Parse for Name {
fn parse(input: ParseStream) -> syn::Result<Self> {
let n1 = input.parse()?;
let n = if input.peek(Colon) {
let colon = input.parse()?;
let name = input.parse()?;
Self {
namespace: Some((n1, colon)),
name,
}
} else {
Self {
namespace: None,
name: n1,
}
};
Ok(n)
}
}
impl Parse for Tag {
fn parse(input: ParseStream) -> syn::Result<Self> {
let s = Tag {
lt: input.parse()?,
name: input.parse()?,
attrs: input.call(parse_many)?,
tail: input.parse()?,
};
if let TagTail::Complex { name2, .. } = &s.tail {
if &s.name != name2 {
return Err(Error::new(name2.span(), format!("Invalid ending tag, expected: {}", s.name)));
}
}
Ok(s)
}
}
impl Parse for TagTail {
fn parse(input: ParseStream) -> syn::Result<Self> {
let look = input.lookahead1();
if look.peek(Div) {
Ok(Self::Simple {
slash: input.parse()?,
gt: input.parse()?,
})
} else if look.peek(Gt) {
fn parse_content<'a>(input: &'a ParseBuffer<'a>) -> syn::Result<Vec<Content>> {
let mut c = Vec::new();
while !(input.peek(Lt) && input.peek2(Div)) {
c.push(input.parse()?);
}
Ok(c)
}
Ok(Self::Complex {
gt: input.parse()?,
inner: Xml(input.call(parse_content)?),
lt: input.parse()?,
slash: input.parse()?,
name2: input.parse()?,
gt2: input.parse()?,
})
} else {
Err(look.error())
}
}
}
impl Parse for Attr {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
name: input.parse()?,
eq: input.parse()?,
value: input.parse()?,
})
}
}
impl Parse for Value {
fn parse(input: ParseStream) -> syn::Result<Self> {
let look = input.lookahead1();
if look.peek(Brace) {
input.parse().map(Self::Block)
} else if look.peek(LitStr) {
input.parse().map(Self::Str)
} else {
Err(look.error())
}
}
}
macro_rules! if_punct {
(@internal $input:ident, $token:tt) => {
{
let tk: Token![$token] = $input.parse()?;
let s = tk.into_token_stream().to_string();
assert_eq!(s, stringify!($token));
let ch = s.chars().next().unwrap();
Ok(Self::Punct(Punct::new(ch, Spacing::Alone)))
}
};
($input:ident, $look:ident, [$first:tt, $($token:tt),+ $(,)?], $else:tt) => {
if $look.peek(Token![$first]) {
if_punct!(@internal $input, $first)
}
$(else if $look.peek(Token![$token]) { if_punct!(@internal $input, $token) })+
else $else
};
}
impl Parse for Content {
fn parse(input: ParseStream) -> syn::Result<Self> {
let look = input.lookahead1();
look.peek(Paren);
if_punct!(input, look, [+, @, !, ^, :, ,, /, ., =, |, #, ?, %, ;, *, -, _], {
if look.peek(Lt) {
input.parse().map(Self::Tag)
} else if look.peek(Brace) {
input.parse().map(Self::Block)
} else if look.peek(Ident::peek_any) {
Ident::parse_any(input).map(Self::Ident)
} else if look.peek(LitStr) {
input.parse().map(Self::Str)
} else if look.peek(LitInt) {
input.parse().map(Self::Int)
} else if look.peek(LitFloat) {
input.parse().map(Self::Float)
} else if look.peek(And) {
Ok(Self::Escape {
amp: input.parse()?,
escape: input.parse()?,
semi: input.parse()?,
})
} else {
return Err(look.error());
}
})
}
}
impl Parse for Escape {
fn parse(input: ParseStream) -> syn::Result<Self> {
let look = input.lookahead1();
if look.peek(Pound) {
let pound: Pound = input.parse()?;
let look = input.lookahead1();
if look.peek(LitInt) {
Ok(Escape::Int { pound, lit: input.parse()? })
} else if look.peek(Ident::peek_any) {
let ident = Ident::parse_any(input)?;
let name = ident.to_string();
let mut iter = name.chars();
if let Some('x') = iter.next() {
let value = iter.map_while(|ch| ch.to_digit(16)).fold(0, |a, b| a * 16 + b);
if value != 0 {
Ok(Escape::X {
pound,
value,
x: ident,
})
} else {
Err(Error::new(ident.span(), "Escape sequence must not be zero."))
}
} else {
Err(Error::new(ident.span(), "Invalid escape sequence, expected: `x`, or decimal"))
}
} else {
Err(look.error())
}
} else if look.peek(Ident::peek_any) {
let ident = Ident::parse_any(input)?;
let x = match ident.to_string().as_str() {
"lt" => Escape::Lt(ident.span()),
"gt" => Escape::Gt(ident.span()),
"amp" => Escape::Amp(ident.span()),
"apos" => Escape::Apos(ident.span()),
"quot" => Escape::Quot(ident.span()),
_ => return Err(Error::new(ident.span(), "Invalid escape sequence, expected: `lt`, `gt`, `amp`, `apos`, `quot`, or `#`")),
};
Ok(x)
} else {
Err(look.error())
}
}
}