use crate::collection::{CellInfo, GridDimensions, NameGenerator};
use syn::parse::{ParseStream, Result};
use syn::{Error, Ident, Token};
use syn::{braced, bracketed};
#[allow(non_camel_case_types)]
mod kw {
use syn::custom_keyword;
custom_keyword!(column);
custom_keyword!(row);
}
pub trait Parser {
type Output;
fn parse(input: ParseStream, core_gen: &mut NameGenerator) -> Result<Self::Output>;
}
pub fn parse_list<P: Parser>(
input: ParseStream,
core_gen: &mut NameGenerator,
) -> Result<Vec<P::Output>> {
let mut items = vec![];
while !input.is_empty() {
items.push(P::parse(input, core_gen)?);
if input.is_empty() {
break;
}
let _: Token![,] = input.parse()?;
}
Ok(items)
}
pub fn parse_grid<P: Parser>(
input: ParseStream,
core_gen: &mut NameGenerator,
) -> Result<(GridDimensions, Vec<CellInfo>, Vec<P::Output>)> {
let mut dim = GridDimensions::default();
let mut infos = vec![];
let mut items = vec![];
while !input.is_empty() {
let mut require_comma = true;
if input.peek2(Token![!]) {
let lookahead = input.lookahead1();
if lookahead.peek(kw::column) {
let _: kw::column = input.parse()?;
let _: Token![!] = input.parse()?;
let inner;
let _ = bracketed!(inner in input);
let col = dim.cols;
let mut row = 0;
while !inner.is_empty() {
if let Ok(_) = inner.parse::<Token![_]>() {
} else {
let layout = P::parse(&inner, core_gen)?;
let cell = CellInfo::new(col, row);
dim.update(&cell);
infos.push(cell);
items.push(layout);
}
row += 1;
if inner.is_empty() {
break;
}
inner.parse::<Token![,]>()?;
}
} else if lookahead.peek(kw::row) {
let _: kw::row = input.parse()?;
let _: Token![!] = input.parse()?;
let inner;
let _ = bracketed!(inner in input);
let mut col = 0;
let row = dim.rows;
while !inner.is_empty() {
if let Ok(_) = inner.parse::<Token![_]>() {
} else {
let layout = P::parse(&inner, core_gen)?;
let cell = CellInfo::new(col, row);
dim.update(&cell);
infos.push(cell);
items.push(layout);
}
col += 1;
if inner.is_empty() {
break;
}
inner.parse::<Token![,]>()?;
}
} else {
let ident: Ident = input.parse()?;
let tok: Token![!] = input.parse()?;
let span = ident.span();
let span = span.join(tok.span).unwrap_or(span);
return Err(Error::new(span, "expected: `column!` or `row!`"));
}
} else {
let cell = input.parse()?;
dim.update(&cell);
let _: Token![=>] = input.parse()?;
let layout;
if input.peek(syn::token::Brace) {
let inner;
let _ = braced!(inner in input);
layout = P::parse(&inner, core_gen)?;
require_comma = false;
} else {
layout = P::parse(input, core_gen)?;
}
infos.push(cell);
items.push(layout);
}
if input.is_empty() {
break;
}
if let Err(e) = input.parse::<Token![,]>() {
if require_comma {
return Err(e);
}
}
}
Ok((dim, infos, items))
}