use std::fmt::Display;
use quote::ToTokens;
use syn::Error;
use syn::bracketed;
use syn::parse::{Parse, ParseStream};
use syn::{Expr, Ident, Result, Token};
pub struct RicciIndexDeclaration {
pub indexes: Vec<Ident>,
}
pub enum RicciIndexer {
Direct {
index: Ident,
},
Reindexing {
reindexing_name: Ident,
indexers: Vec<RicciIndexer>,
},
Reverse {
index: Ident,
},
Plain {
expr: Box<Expr>,
},
}
pub struct RicciAliasDeclaration {
pub index: Ident,
pub indexer: RicciIndexer,
}
impl Parse for RicciIndexDeclaration {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<Token![for]>()?;
let mut indexes: Vec<Ident> = Vec::new();
let index: Ident = input.parse()?;
indexes.push(index);
while input.peek(syn::Ident) {
let ident: Ident = input.parse()?;
indexes.push(ident);
}
Ok(Self { indexes })
}
}
fn parse_keyword_indexing(keyword_token: Ident, input: ParseStream) -> Result<RicciIndexer> {
if keyword_token == "plain" {
let expr: Box<Expr> = input.parse()?;
Ok(RicciIndexer::Plain { expr })
} else if keyword_token == "rev" {
let index: Ident = input.parse()?;
Ok(RicciIndexer::Reverse { index })
} else {
Err(Error::new(
keyword_token.span(),
format!("Unknown indexing keyword: {}", keyword_token),
))
}
}
fn parse_external_reindexing(reindexing_name: Ident, input: ParseStream) -> Result<RicciIndexer> {
let content;
bracketed!(content in input);
let mut indexers: Vec<RicciIndexer> = Vec::new();
let mut first = true;
while !content.is_empty() {
if first {
first = false;
} else {
content.parse::<Token![,]>()?;
}
let indexer: RicciIndexer = content.parse()?;
indexers.push(indexer);
}
Ok(RicciIndexer::Reindexing {
reindexing_name,
indexers,
})
}
impl Parse for RicciIndexer {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(syn::Ident) {
let keyword_token: Ident = input.parse()?;
if input.is_empty() {
Ok(RicciIndexer::Direct {
index: keyword_token,
})
} else if input.peek(Token![:]) {
input.parse::<Token![:]>()?;
parse_keyword_indexing(keyword_token, input)
} else if input.peek(syn::token::Bracket) {
parse_external_reindexing(keyword_token, input)
} else {
Ok(RicciIndexer::Direct {
index: keyword_token,
})
}
} else {
Err(Error::new(
input.span(),
"Expected an identifier for indexing",
))
}
}
}
impl Parse for RicciAliasDeclaration {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<Token![let]>()?;
let index: Ident = input.parse()?;
input.parse::<Token![=]>()?;
let indexer: RicciIndexer = input.parse()?;
Ok(Self { index, indexer })
}
}
impl Display for RicciIndexer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RicciIndexer::Direct { index } => write!(f, " {}", index),
RicciIndexer::Reindexing {
reindexing_name,
indexers,
} => {
f.write_fmt(format_args!(" {} ⦇", reindexing_name))?;
let mut first = true;
for indexer in indexers {
if !first {
f.write_str(" ,")?;
}
first = false;
indexer.fmt(f)?;
}
f.write_str(" ⦈")
}
RicciIndexer::Reverse { index } => f.write_fmt(format_args!(" ↺ {}", index)),
RicciIndexer::Plain { expr } => {
f.write_str(" « ")?;
expr.to_token_stream().fmt(f)?;
f.write_str(" »")
}
}
}
}
impl Display for RicciIndexDeclaration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("∀")?;
for index in &self.indexes {
f.write_fmt(format_args!(" {}", index))?;
}
Ok(())
}
}
impl Display for RicciAliasDeclaration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("∙ {} ≔{}", self.index, self.indexer))
}
}
#[cfg(test)]
mod tests {
use crate::model::header::{RicciAliasDeclaration, RicciIndexDeclaration};
use quote::quote;
#[test]
fn parse_header() {
let tokens = quote!(for i j);
assert_eq!(
asserts::parse_and_display::<RicciIndexDeclaration>(tokens),
"∀ i j"
);
let tokens = quote!(let i = plain: 4 + g());
assert_eq!(
asserts::parse_and_display::<RicciAliasDeclaration>(tokens),
"∙ i ≔ « 4 + g () »"
);
let tokens = quote!(let i = rev: long_index_name);
assert_eq!(
asserts::parse_and_display::<RicciAliasDeclaration>(tokens),
"∙ i ≔ ↺ long_index_name"
);
let tokens = quote!(let i = f[a, b, c]);
assert_eq!(
asserts::parse_and_display::<RicciAliasDeclaration>(tokens),
"∙ i ≔ f ⦇ a , b , c ⦈"
);
}
}