use super::{
Address, Declaration, FactMapping, FamilySlug, MutationCaptureError, Permission, Policy,
};
use crate::descriptor::{
CaptureCause, DeclarationError, Grammar, ModuleName, Name, SupportName, TypeName,
};
use crate::token::{CapturedDelimiter, CapturedTokenTree, SpanHandle};
const MODULE: &str = "module";
const REFUSAL: &str = "refusal";
const SUPPORT: &str = "support";
const FAMILY: &str = "family";
const POINT: &str = "point";
const FACT: &str = "fact";
const MAP: &str = "map";
const PERMIT: &str = "permit";
const NAMED: &str = "named";
const DECLARABLE: [&str; 6] = [MODULE, REFUSAL, SUPPORT, FAMILY, POINT, FACT];
pub fn captured(
body: &[&CapturedTokenTree],
at: SpanHandle,
grammar: Grammar,
) -> Result<Declaration, MutationCaptureError> {
let clauses = clauses(grammar, body)?;
let address = Address {
module: ModuleName::declared(identifier(grammar, &clauses, MODULE, at)?)
.map_err(|refusal| carried(grammar, refusal, at))?,
support: optional_support(grammar, &clauses)?,
refusal: TypeName::declared(identifier(grammar, &clauses, REFUSAL, at)?)
.map_err(|refusal| carried(grammar, refusal, at))?,
};
let family = named_reference(grammar, &clauses, FAMILY, at)?;
let point = named_reference(grammar, &clauses, POINT, at)?;
let fact = named_reference(grammar, &clauses, FACT, at)?;
let mut mappings: Vec<FactMapping> = Vec::new();
let mut permissions: Vec<Permission> = Vec::new();
for clause in &clauses {
match *clause {
Clause::Mapping {
fact: ref mapped,
ref claim,
} => mappings.push(FactMapping {
fact: mapped.clone(),
claim: claim.clone(),
}),
Clause::Permission {
ref claim,
ref families,
at: site,
} => permissions.push(
Permission::permitted(claim.clone(), families.clone())
.map_err(|refusal| carried(grammar, refusal, site))?,
),
Clause::Assigned { .. } => {}
}
}
let policy = Policy::declared(family, mappings, permissions)
.map_err(|refusal| carried(grammar, refusal, at))?;
Ok(Declaration::captured(address, policy, point, fact))
}
const fn refused(grammar: Grammar, cause: CaptureCause, at: SpanHandle) -> MutationCaptureError {
MutationCaptureError::grammar_refused(grammar, cause, at)
}
const fn carried(
grammar: Grammar,
refusal: DeclarationError,
at: SpanHandle,
) -> MutationCaptureError {
MutationCaptureError::vocabulary_refused(grammar, refusal, at)
}
enum Clause<'trees> {
Assigned {
key: &'trees str,
value: Vec<&'trees CapturedTokenTree>,
at: SpanHandle,
},
Mapping {
fact: Name,
claim: Name,
},
Permission {
claim: Name,
families: Vec<FamilySlug>,
at: SpanHandle,
},
}
fn clauses<'trees>(
grammar: Grammar,
body: &[&'trees CapturedTokenTree],
) -> Result<Vec<Clause<'trees>>, MutationCaptureError> {
let mut read: 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(),
));
}
read.push(clause(grammar, &group)?);
group.clear();
} else {
group.push(tree);
}
}
if !group.is_empty() {
read.push(clause(grammar, &group)?);
}
distinct(grammar, &read)?;
Ok(read)
}
fn clause<'trees>(
grammar: Grammar,
group: &[&'trees CapturedTokenTree],
) -> Result<Clause<'trees>, MutationCaptureError> {
let Some((head, rest)) = group.split_first() else {
return Err(refused(
grammar,
CaptureCause::ClauseUnread,
SpanHandle::at(0),
));
};
match head.word() {
Some(MAP) => mapping(grammar, rest, head.span()),
Some(PERMIT) => permission(grammar, rest, head.span()),
Some(key) if DECLARABLE.contains(&key) => assignment(grammar, key, rest, head.span()),
Some(_) => Err(refused(
grammar,
CaptureCause::ClauseUndeclared,
head.span(),
)),
None => Err(refused(grammar, CaptureCause::ClauseUnread, head.span())),
}
}
fn assignment<'trees>(
grammar: Grammar,
key: &'trees str,
rest: &[&'trees CapturedTokenTree],
at: SpanHandle,
) -> Result<Clause<'trees>, MutationCaptureError> {
let Some((assigned_by, value)) = rest.split_first() else {
return Err(refused(grammar, CaptureCause::ClauseUnread, at));
};
if assigned_by.punct() != Some('=') || value.is_empty() {
return Err(refused(
grammar,
CaptureCause::ClauseUnread,
assigned_by.span(),
));
}
Ok(Clause::Assigned {
key,
value: value.to_vec(),
at,
})
}
fn mapping<'trees>(
grammar: Grammar,
rest: &[&CapturedTokenTree],
at: SpanHandle,
) -> Result<Clause<'trees>, MutationCaptureError> {
let [
fact_word,
fact_arguments,
assigned_by,
claim_word,
claim_arguments,
] = rest
else {
return Err(refused(grammar, CaptureCause::MappingUnread, at));
};
if assigned_by.punct() != Some('=') {
return Err(refused(
grammar,
CaptureCause::MappingUnread,
assigned_by.span(),
));
}
Ok(Clause::Mapping {
fact: named_value(grammar, fact_word, fact_arguments)?,
claim: named_value(grammar, claim_word, claim_arguments)?,
})
}
fn permission<'trees>(
grammar: Grammar,
rest: &[&CapturedTokenTree],
at: SpanHandle,
) -> Result<Clause<'trees>, MutationCaptureError> {
let [word, arguments, assigned_by, bracketed] = rest else {
return Err(refused(grammar, CaptureCause::PermissionUnread, at));
};
if assigned_by.punct() != Some('=') {
return Err(refused(
grammar,
CaptureCause::PermissionUnread,
assigned_by.span(),
));
}
let claim = named_value(grammar, word, arguments)?;
let Some((CapturedDelimiter::Bracket, inner)) = bracketed.group() else {
return Err(refused(
grammar,
CaptureCause::PermissionUnread,
bracketed.span(),
));
};
let mut families: Vec<FamilySlug> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in inner {
if tree.punct() == Some(',') {
families.push(separated_family(grammar, &group, tree.span())?);
group.clear();
} else {
group.push(tree);
}
}
if let Some(last) = trailing_family(grammar, &group)? {
families.push(last);
}
Ok(Clause::Permission {
claim,
families,
at,
})
}
fn separated_family(
grammar: Grammar,
group: &[&CapturedTokenTree],
at: SpanHandle,
) -> Result<FamilySlug, MutationCaptureError> {
match group {
[] => Err(refused(grammar, CaptureCause::SeparatorDangling, at)),
[only] => family(grammar, only),
[first, ..] => Err(refused(
grammar,
CaptureCause::PermissionUnread,
first.span(),
)),
}
}
fn trailing_family(
grammar: Grammar,
group: &[&CapturedTokenTree],
) -> Result<Option<FamilySlug>, MutationCaptureError> {
match group {
[] => Ok(None),
[only] => family(grammar, only).map(Some),
[first, ..] => Err(refused(
grammar,
CaptureCause::PermissionUnread,
first.span(),
)),
}
}
fn family(grammar: Grammar, tree: &CapturedTokenTree) -> Result<FamilySlug, MutationCaptureError> {
let Some(slug) = tree.text() else {
return Err(refused(
grammar,
CaptureCause::PermissionUnread,
tree.span(),
));
};
FamilySlug::declared(slug).map_err(|refusal| carried(grammar, refusal, tree.span()))
}
fn distinct(grammar: Grammar, clauses: &[Clause<'_>]) -> Result<(), MutationCaptureError> {
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::Mapping { .. } | Clause::Permission { .. } => 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::Mapping { .. } | Clause::Permission { .. } => None,
})
}
fn identifier<'trees>(
grammar: Grammar,
clauses: &[Clause<'trees>],
key: &str,
at: SpanHandle,
) -> Result<&'trees str, MutationCaptureError> {
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 optional_support(
grammar: Grammar,
clauses: &[Clause<'_>],
) -> Result<Option<SupportName>, MutationCaptureError> {
let Some((value, at)) = assigned(clauses, SUPPORT) else {
return Ok(None);
};
let [only] = value else {
return Err(refused(grammar, CaptureCause::ClauseUnread, at));
};
let spelling = only
.word()
.ok_or_else(|| refused(grammar, CaptureCause::ClauseUnread, only.span()))?;
SupportName::declared(spelling)
.map(Some)
.map_err(|refusal| carried(grammar, refusal, only.span()))
}
fn named_reference(
grammar: Grammar,
clauses: &[Clause<'_>],
key: &str,
at: SpanHandle,
) -> Result<Name, MutationCaptureError> {
let (value, clause) =
assigned(clauses, key).ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
let [word, arguments] = value else {
return Err(refused(grammar, CaptureCause::ReferenceUnread, clause));
};
named_value(grammar, word, arguments)
}
fn named_value(
grammar: Grammar,
word: &CapturedTokenTree,
arguments: &CapturedTokenTree,
) -> Result<Name, MutationCaptureError> {
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()))
}