use super::{References, Row, SuiteGroup, TrialCaptureError, Trials};
use crate::descriptor::clause::{
Clause, assigned, assignment_clauses, declaration_clauses, identifier, named_reference,
named_value,
};
use crate::descriptor::{
CaptureCause, DeclarationError, FunctionName, Grammar, ModuleName, Name, SupportName,
};
use crate::token::{CapturedDelimiter, CapturedTokenTree, SpanHandle};
use core::convert::Infallible;
const SUPPORT: &str = "support";
const MODULE: &str = "module";
const TABLE: &str = "table";
const SUITE: &str = "suite";
const CLAIM: &str = "claim";
const ROLES: &str = "roles";
const TAGS: &str = "tags";
const SUBJECT: &str = "subject";
const CHECK: &str = "check";
const POPULATION: &str = "population";
const DECLARABLE: [&str; 3] = [SUPPORT, MODULE, TABLE];
const DECLARABLE_ROW: [&str; 6] = [CLAIM, ROLES, TAGS, SUBJECT, CHECK, POPULATION];
pub fn captured(
body: &[&CapturedTokenTree],
at: SpanHandle,
grammar: Grammar,
) -> Result<Trials, TrialCaptureError> {
let clauses = declaration_clauses(grammar, body, &DECLARABLE, suite_clause, refused)?;
let support = SupportName::declared(identifier(grammar, &clauses, SUPPORT, at, refused)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let module = ModuleName::declared(identifier(grammar, &clauses, MODULE, at, refused)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let table = named_reference(grammar, &clauses, TABLE, at, refused, carried)?;
let mut groups: Vec<SuiteGroup> = Vec::new();
for clause in &clauses {
if let Some(suite) = clause.nested_value() {
groups.push(suite_group(grammar, suite.seat, &suite.suite, &suite.rows)?);
}
}
Trials::declared(support, module, table, groups)
.map_err(|refusal| carried(grammar, refusal, at))
}
const fn refused(grammar: Grammar, cause: CaptureCause, at: SpanHandle) -> TrialCaptureError {
TrialCaptureError::grammar_refused(grammar, cause, at)
}
const fn carried(grammar: Grammar, refusal: DeclarationError, at: SpanHandle) -> TrialCaptureError {
TrialCaptureError::vocabulary_refused(grammar, refusal, at)
}
struct SuiteClause<'trees> {
seat: &'trees CapturedTokenTree,
suite: Vec<&'trees CapturedTokenTree>,
rows: Vec<&'trees CapturedTokenTree>,
}
fn suite_clause<'trees>(
grammar: Grammar,
group: &[&'trees CapturedTokenTree],
) -> Result<Option<SuiteClause<'trees>>, TrialCaptureError> {
let Some((head, rest)) = group.split_first() else {
return Ok(None);
};
if head.word() != Some(SUITE) {
return Ok(None);
}
let opening = head.span();
let malformed = || refused(grammar, CaptureCause::GroupUnread, opening);
let (seat, after_seat) = rest.split_first().ok_or_else(malformed)?;
if seat.word().is_none() {
return Err(refused(grammar, CaptureCause::GroupUnread, seat.span()));
}
let (assigned_by, after_assignment) = after_seat.split_first().ok_or_else(malformed)?;
if assigned_by.punct() != Some('=') {
return Err(refused(
grammar,
CaptureCause::GroupUnread,
assigned_by.span(),
));
}
let (body, suite) = after_assignment.split_last().ok_or_else(malformed)?;
match body.group() {
Some((CapturedDelimiter::Brace, inner)) => Ok(Some(SuiteClause {
seat,
suite: suite.to_vec(),
rows: inner.iter().collect(),
})),
Some(_) | None => Err(refused(grammar, CaptureCause::GroupUnread, body.span())),
}
}
fn roster(
grammar: Grammar,
clauses: &[Clause<'_, Infallible>],
key: &str,
) -> Result<Vec<Name>, TrialCaptureError> {
let Some((value, at)) = assigned(clauses, key) else {
return Ok(Vec::new());
};
let [bracketed] = value else {
return Err(refused(grammar, CaptureCause::RosterUnread, at));
};
let Some((CapturedDelimiter::Bracket, inner)) = bracketed.group() else {
return Err(refused(
grammar,
CaptureCause::RosterUnread,
bracketed.span(),
));
};
let mut named: Vec<Name> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in inner {
if tree.punct() == Some(',') {
if group.is_empty() {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
}
named.push(named_value(
grammar,
&group,
bracketed.span(),
refused,
carried,
)?);
group.clear();
} else {
group.push(tree);
}
}
if !group.is_empty() {
named.push(named_value(
grammar,
&group,
bracketed.span(),
refused,
carried,
)?);
}
Ok(named)
}
fn suite_group(
grammar: Grammar,
seat: &CapturedTokenTree,
suite: &[&CapturedTokenTree],
rows: &[&CapturedTokenTree],
) -> Result<SuiteGroup, TrialCaptureError> {
let at = seat.span();
let spelling = seat
.word()
.ok_or_else(|| refused(grammar, CaptureCause::GroupUnread, at))?;
let named =
FunctionName::declared(spelling).map_err(|refusal| carried(grammar, refusal, at))?;
let selected = named_value(grammar, suite, at, refused, carried)?;
let mut declared: Vec<Row> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in rows {
if tree.punct() == Some(',') {
if group.is_empty() {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
}
declared.push(row(grammar, &group, at)?);
group.clear();
} else {
group.push(tree);
}
}
if !group.is_empty() {
declared.push(row(grammar, &group, at)?);
}
SuiteGroup::declared(named, selected, declared).map_err(|refusal| carried(grammar, refusal, at))
}
fn row(
grammar: Grammar,
group: &[&CapturedTokenTree],
seat: SpanHandle,
) -> Result<Row, TrialCaptureError> {
let [named, body] = group else {
let at = group.first().map_or(seat, |tree| tree.span());
return Err(refused(grammar, CaptureCause::RowUnread, at));
};
let at = named.span();
let spelling = named
.word()
.ok_or_else(|| refused(grammar, CaptureCause::RowUnread, at))?;
let lens = FunctionName::declared(spelling).map_err(|refusal| carried(grammar, refusal, at))?;
let Some((CapturedDelimiter::Brace, inner)) = body.group() else {
return Err(refused(grammar, CaptureCause::RowUnread, body.span()));
};
let trees: Vec<&CapturedTokenTree> = inner.iter().collect();
let clauses = assignment_clauses(grammar, &trees, &DECLARABLE_ROW, refused)?;
let references = References {
claim: named_reference(grammar, &clauses, CLAIM, at, refused, carried)?,
subject: named_reference(grammar, &clauses, SUBJECT, at, refused, carried)?,
check: named_reference(grammar, &clauses, CHECK, at, refused, carried)?,
population: named_reference(grammar, &clauses, POPULATION, at, refused, carried)?,
};
let roles = roster(grammar, &clauses, ROLES)?;
let tags = roster(grammar, &clauses, TAGS)?;
Row::declared(lens, references, roles, tags).map_err(|refusal| carried(grammar, refusal, at))
}