use super::{References, Row, SuiteGroup, TrialCaptureError, Trials};
use crate::descriptor::{
CaptureCause, DeclarationError, FunctionName, Grammar, ModuleName, Name, SupportName,
};
use crate::token::{CapturedDelimiter, CapturedTokenTree, SpanHandle};
const SUPPORT: &str = "support";
const MODULE: &str = "module";
const TABLE: &str = "table";
const SUITE: &str = "suite";
const NAMED: &str = "named";
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)?;
let support = SupportName::declared(identifier(grammar, &clauses, SUPPORT, at)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let module = ModuleName::declared(identifier(grammar, &clauses, MODULE, at)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let table = named_reference(grammar, &clauses, TABLE, at)?;
let mut groups: Vec<SuiteGroup> = Vec::new();
for clause in &clauses {
if let Clause::Suite { seat, suite, rows } = clause {
groups.push(suite_group(grammar, seat, 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)
}
enum Clause<'trees> {
Assigned {
key: &'trees str,
value: Vec<&'trees CapturedTokenTree>,
at: SpanHandle,
},
Suite {
seat: &'trees CapturedTokenTree,
suite: Vec<&'trees CapturedTokenTree>,
rows: Vec<&'trees CapturedTokenTree>,
},
}
fn declaration_clauses<'trees>(
grammar: Grammar,
body: &[&'trees CapturedTokenTree],
) -> Result<Vec<Clause<'trees>>, TrialCaptureError> {
let mut clauses: Vec<Clause<'trees>> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in body {
if tree.punct() == Some(',') {
if group.is_empty() {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
}
close(grammar, &group, &mut clauses)?;
group.clear();
} else {
group.push(tree);
}
}
close(grammar, &group, &mut clauses)?;
distinct(grammar, &clauses)?;
Ok(clauses)
}
fn close<'trees>(
grammar: Grammar,
group: &[&'trees CapturedTokenTree],
clauses: &mut Vec<Clause<'trees>>,
) -> Result<(), TrialCaptureError> {
let Some((head, rest)) = group.split_first() else {
return Ok(());
};
if head.word() == Some(SUITE) {
clauses.push(suite_clause(grammar, rest, head.span())?);
return Ok(());
}
clauses.push(assignment(grammar, head, rest, &DECLARABLE)?);
Ok(())
}
fn assignment<'trees>(
grammar: Grammar,
head: &'trees CapturedTokenTree,
rest: &[&'trees CapturedTokenTree],
declarable: &[&str],
) -> Result<Clause<'trees>, TrialCaptureError> {
let opening = head.span();
let Some(key) = head.word() else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening));
};
let Some((assigned_by, value)) = rest.split_first() else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening));
};
if assigned_by.punct() != Some('=') || value.is_empty() {
return Err(refused(
grammar,
CaptureCause::ClauseUnread,
assigned_by.span(),
));
}
if !declarable.contains(&key) {
return Err(refused(grammar, CaptureCause::ClauseUndeclared, opening));
}
Ok(Clause::Assigned {
key,
value: value.to_vec(),
at: opening,
})
}
fn suite_clause<'trees>(
grammar: Grammar,
rest: &[&'trees CapturedTokenTree],
opening: SpanHandle,
) -> Result<Clause<'trees>, TrialCaptureError> {
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(Clause::Suite {
seat,
suite: suite.to_vec(),
rows: inner.iter().collect(),
}),
Some(_) | None => Err(refused(grammar, CaptureCause::GroupUnread, body.span())),
}
}
fn distinct(grammar: Grammar, clauses: &[Clause<'_>]) -> Result<(), TrialCaptureError> {
for (position, clause) in clauses.iter().enumerate() {
let Clause::Assigned { key, at, .. } = clause else {
continue;
};
let earlier = clauses.iter().take(position).any(|other| match *other {
Clause::Assigned { key: seen, .. } => seen == *key,
Clause::Suite { .. } => false,
});
if earlier {
return Err(refused(grammar, CaptureCause::ClauseDoubled, *at));
}
}
Ok(())
}
fn assigned<'trees, 'clauses>(
clauses: &'clauses [Clause<'trees>],
key: &str,
) -> Option<(&'clauses [&'trees CapturedTokenTree], SpanHandle)> {
clauses.iter().find_map(|clause| match *clause {
Clause::Assigned {
key: named,
ref value,
at,
} if named == key => Some((value.as_slice(), at)),
Clause::Assigned { .. } | Clause::Suite { .. } => None,
})
}
fn identifier<'trees>(
grammar: Grammar,
clauses: &[Clause<'trees>],
key: &str,
at: SpanHandle,
) -> Result<&'trees str, TrialCaptureError> {
let (value, clause) =
assigned(clauses, key).ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
let [only] = value else {
return Err(refused(grammar, CaptureCause::ClauseUnread, clause));
};
only.word()
.ok_or_else(|| refused(grammar, CaptureCause::ClauseUnread, only.span()))
}
fn named_reference(
grammar: Grammar,
clauses: &[Clause<'_>],
key: &str,
at: SpanHandle,
) -> Result<Name, TrialCaptureError> {
let (value, clause) =
assigned(clauses, key).ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
named_value(grammar, value, clause)
}
fn named_value(
grammar: Grammar,
value: &[&CapturedTokenTree],
at: SpanHandle,
) -> Result<Name, TrialCaptureError> {
let [word, arguments] = value else {
return Err(refused(grammar, CaptureCause::ReferenceUnread, at));
};
if word.word() != Some(NAMED) {
return Err(refused(grammar, CaptureCause::ReferenceUnread, word.span()));
}
let Some((CapturedDelimiter::Parenthesis, inner)) = arguments.group() else {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
arguments.span(),
));
};
let parts: Vec<&CapturedTokenTree> = inner.iter().collect();
let [namespace, separator, stem] = parts.as_slice() else {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
arguments.span(),
));
};
if separator.punct() != Some(',') {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
separator.span(),
));
}
let (Some(owner), Some(spelling)) = (namespace.text(), stem.text()) else {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
arguments.span(),
));
};
Name::named(owner, spelling).map_err(|refusal| carried(grammar, refusal, arguments.span()))
}
fn roster(
grammar: Grammar,
clauses: &[Clause<'_>],
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())?);
group.clear();
} else {
group.push(tree);
}
}
if !group.is_empty() {
named.push(named_value(grammar, &group, bracketed.span())?);
}
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)?;
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 = row_clauses(grammar, &trees)?;
let references = References {
claim: named_reference(grammar, &clauses, CLAIM, at)?,
subject: named_reference(grammar, &clauses, SUBJECT, at)?,
check: named_reference(grammar, &clauses, CHECK, at)?,
population: named_reference(grammar, &clauses, POPULATION, at)?,
};
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))
}
fn row_clauses<'trees>(
grammar: Grammar,
body: &[&'trees CapturedTokenTree],
) -> Result<Vec<Clause<'trees>>, TrialCaptureError> {
let mut clauses: Vec<Clause<'trees>> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in body {
if tree.punct() == Some(',') {
let Some((head, rest)) = group.split_first() else {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
};
clauses.push(assignment(grammar, head, rest, &DECLARABLE_ROW)?);
group.clear();
} else {
group.push(tree);
}
}
if let Some((head, rest)) = group.split_first() {
clauses.push(assignment(grammar, head, rest, &DECLARABLE_ROW)?);
}
distinct(grammar, &clauses)?;
Ok(clauses)
}