use proc_macro2::Span;
use syn::{
Result, Token, parenthesized,
parse::{Parse, ParseStream},
};
use super::types::{Access, EqualityOper, Reference, TermIdent, TermOper, peek_id, peek_trav};
pub struct TermId {
pub ident: Option<TermIdent>,
pub trav_self: bool,
pub trav_up: bool,
pub up_ident: Option<TermIdent>,
pub trav_desc: bool,
pub trav_cascade: bool,
pub cascade_ident: Option<TermIdent>,
pub span: Span,
}
impl TermId {
pub(crate) fn new(ident: Option<TermIdent>, span: Span) -> Self {
Self {
ident,
trav_self: false,
trav_up: false,
up_ident: None,
trav_desc: false,
trav_cascade: false,
cascade_ident: None,
span,
}
}
}
impl Parse for TermId {
fn parse(input: ParseStream) -> Result<Self> {
use super::types::kw;
use syn::Ident;
let span: Span = input.span();
let ident = if !peek_trav(input) {
let ident = input.parse::<TermIdent>()?;
if input.peek(Token![|]) && !input.peek2(Token![|]) {
input.parse::<Token![|]>()?;
}
Some(ident)
} else {
None
};
let mut out = Self::new(ident, span);
while peek_trav(input) {
if input.peek(kw::cascade) {
input.parse::<kw::cascade>()?;
out.trav_cascade = true;
if input.peek(Ident) || input.peek(Token![$]) {
out.cascade_ident = Some(input.parse::<TermIdent>()?);
}
}
if input.peek(kw::desc) {
input.parse::<kw::desc>()?;
out.trav_desc = true;
}
if input.peek(kw::up) {
input.parse::<kw::up>()?;
out.trav_up = true;
if input.peek(Ident) || input.peek(Token![$]) {
out.up_ident = Some(input.parse::<TermIdent>()?);
}
}
if input.peek(Token![self]) {
input.parse::<Token![self]>()?;
out.trav_self = true;
}
if input.peek(Token![|]) && !input.peek2(Token![|]) {
input.parse::<Token![|]>()?;
}
}
Ok(out)
}
}
#[allow(clippy::large_enum_variant)]
pub enum TermType {
Pair(TermId, TermId),
Component(TermId),
Equality(EqualityExpr),
Scope(Vec<Term>),
}
pub struct EqualityExpr {
pub left: TermIdent,
pub oper: super::types::EqualityOper,
pub right: TermIdent,
}
pub struct Term {
pub access: Access,
pub reference: Reference,
pub oper: TermOper,
pub source: TermId,
pub ty: TermType,
pub span: Span,
}
impl Parse for Term {
fn parse(input: ParseStream) -> Result<Self> {
let span = input.span();
let access = input.parse::<Access>()?;
let mut oper = input.parse::<TermOper>()?;
let reference = input.parse::<Reference>()?;
if input.peek(syn::token::Brace) {
let scope_content;
syn::braced!(scope_content in input);
let mut scope_terms = Vec::new();
while !scope_content.is_empty() {
scope_terms.push(scope_content.parse::<Term>()?);
if scope_content.peek(Token![,]) {
scope_content.parse::<Token![,]>()?;
} else if !scope_content.is_empty() {
break;
}
}
return Ok(Term {
access,
reference,
oper,
source: TermId::new(None, input.span()),
ty: TermType::Scope(scope_terms),
span,
});
}
if input.peek(Token![$]) {
let lookahead = input.fork();
let _left = lookahead.parse::<TermIdent>();
if _left.is_ok()
&& (lookahead.peek(Token![==])
|| lookahead.peek(Token![!=])
|| (lookahead.peek(Token![~]) && lookahead.peek2(Token![=])))
{
let left = input.parse::<TermIdent>()?;
let equality_oper = if input.peek(Token![==]) {
input.parse::<Token![==]>()?;
EqualityOper::Equal
} else if input.peek(Token![!=]) {
input.parse::<Token![!=]>()?;
oper = TermOper::Not;
EqualityOper::NotEqual
} else if input.peek(Token![~]) {
input.parse::<Token![~]>()?;
input.parse::<Token![=]>()?;
EqualityOper::Match
} else {
unreachable!()
};
let right = input.parse::<TermIdent>()?;
if equality_oper == EqualityOper::Match
&& let TermIdent::Literal(lit) = &right
&& lit.value().starts_with('!')
{
oper = TermOper::Not;
}
return Ok(Term {
access,
reference,
oper,
source: TermId::new(None, input.span()),
ty: TermType::Equality(EqualityExpr {
left,
oper: equality_oper,
right,
}),
span,
});
}
}
if peek_id(&input) {
let initial = input.parse::<TermId>()?;
if !input.peek(Token![,]) && !input.peek(Token![|]) && !input.is_empty() {
let inner;
parenthesized!(inner in input);
let source = inner.parse::<TermId>()?;
if inner.peek(Token![,]) {
inner.parse::<Token![,]>()?;
let second = inner.parse::<TermId>()?;
Ok(Term {
access,
reference,
oper,
source,
ty: TermType::Pair(initial, second),
span,
})
} else {
Ok(Term {
access,
reference,
oper,
source,
ty: TermType::Component(initial),
span,
})
}
} else {
Ok(Term {
access,
reference,
oper,
source: TermId::new(None, input.span()),
ty: TermType::Component(initial),
span,
})
}
} else {
let inner;
parenthesized!(inner in input);
let first = inner.parse::<TermId>()?;
inner.parse::<Token![,]>()?;
let second = inner.parse::<TermId>()?;
Ok(Term {
access,
reference,
oper,
source: TermId::new(None, input.span()),
ty: TermType::Pair(first, second),
span,
})
}
}
}