use super::render::RESERVED;
use super::{
DisciplineRow, FaultRow, LinkRow, NetworkCaptureError, NetworkDeclaration, ScheduleRow,
};
use crate::descriptor::DirectBinding;
use crate::descriptor::clause::{
assigned_identifier, assigned_text, binding_once, comma_groups, fill_once, opening, value_of,
};
use crate::descriptor::{CaptureCause, Grammar};
use crate::token::{
CapturedDelimiter, CapturedInput, CapturedTokenTree, SpanHandle, rendered_identifier,
rust_keyword,
};
pub fn declared(
body: &CapturedInput,
grammar: Grammar,
) -> Result<NetworkDeclaration, NetworkCaptureError> {
let groups = comma_groups(grammar, body.trees(), refused)?;
let world = world_of(grammar, &groups)?;
let mut schedules: Vec<ScheduleRow> = Vec::new();
for group in &groups {
if head_word(group) == Some("schedule") {
let schedule = schedule_of(grammar, group, &world)?;
if schedules.iter().any(|held| held.name() == schedule.name()) {
return Err(refused(
grammar,
CaptureCause::ChoiceDoubled,
opening(group),
));
}
schedules.push(schedule);
}
}
Ok(NetworkDeclaration::read(
world.harness,
world.module,
world.namespace,
world.nodes,
world.links,
schedules,
))
}
const fn refused(grammar: Grammar, cause: CaptureCause, at: SpanHandle) -> NetworkCaptureError {
NetworkCaptureError::grammar_refused(grammar, cause, at)
}
struct World {
harness: DirectBinding,
module: String,
namespace: String,
nodes: Vec<String>,
links: Vec<LinkRow>,
}
fn head_word<'trees>(group: &[&'trees CapturedTokenTree]) -> Option<&'trees str> {
group.first().and_then(|tree| tree.word())
}
fn world_of(
grammar: Grammar,
groups: &[Vec<&CapturedTokenTree>],
) -> Result<World, NetworkCaptureError> {
let mut module: Option<String> = None;
let mut namespace: Option<String> = None;
let mut nodes: Option<Vec<String>> = None;
let mut harness: Option<DirectBinding> = None;
for group in groups {
match head_word(group) {
Some("harness") => binding_once(
grammar,
group,
&mut harness,
refused,
NetworkCaptureError::binding_refused,
)?,
Some("module") => {
fill_once(grammar, group, &mut module, assigned_identifier, refused)?;
}
Some("namespace") => {
fill_once(grammar, group, &mut namespace, assigned_text, refused)?;
}
Some("nodes") => read_nodes(grammar, group, &mut nodes)?,
Some("link" | "schedule") => {}
Some(_) => {
return Err(refused(
grammar,
CaptureCause::ClauseUndeclared,
opening(group),
));
}
None => return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group))),
}
}
let Some(nodes) = nodes else {
return Err(refused(
grammar,
CaptureCause::ClauseAbsent,
SpanHandle::at(0),
));
};
let Some(harness) = harness else {
return Err(refused(
grammar,
CaptureCause::ClauseAbsent,
SpanHandle::at(0),
));
};
let mut links: Vec<LinkRow> = Vec::new();
for group in groups {
if head_word(group) == Some("link") {
let link = link_of(grammar, group, &nodes)?;
if links.iter().any(|held| held.name() == link.name()) {
return Err(refused(
grammar,
CaptureCause::ChoiceDoubled,
opening(group),
));
}
links.push(link);
}
}
let Some(module) = module else {
return Err(refused(
grammar,
CaptureCause::ClauseAbsent,
SpanHandle::at(0),
));
};
let Some(namespace) = namespace else {
return Err(refused(
grammar,
CaptureCause::ClauseAbsent,
SpanHandle::at(0),
));
};
if links.is_empty() {
return Err(refused(
grammar,
CaptureCause::ClauseAbsent,
SpanHandle::at(0),
));
}
Ok(World {
harness,
module,
namespace,
nodes,
links,
})
}
fn read_nodes(
grammar: Grammar,
group: &[&CapturedTokenTree],
nodes: &mut Option<Vec<String>>,
) -> Result<(), NetworkCaptureError> {
if nodes.is_some() {
return Err(refused(
grammar,
CaptureCause::ClauseDoubled,
opening(group),
));
}
let [roster] = value_of(group) else {
return Err(refused(grammar, CaptureCause::RosterUnread, opening(group)));
};
let Some((CapturedDelimiter::Bracket, members)) = roster.group() else {
return Err(refused(grammar, CaptureCause::RosterUnread, roster.span()));
};
let mut declared: Vec<String> = Vec::new();
let mut separated = true;
for member in members {
if separated {
let Some(word) = member.word() else {
return Err(refused(grammar, CaptureCause::ChoiceUnread, member.span()));
};
if declared.iter().any(|held| held == word) {
return Err(refused(grammar, CaptureCause::ChoiceDoubled, member.span()));
}
declared.push(word.to_owned());
separated = false;
} else {
if member.punct() != Some(',') {
return Err(refused(grammar, CaptureCause::ChoiceUnread, member.span()));
}
separated = true;
}
}
if declared.is_empty() {
return Err(refused(grammar, CaptureCause::NothingChosen, roster.span()));
}
*nodes = Some(declared);
Ok(())
}
fn link_of(
grammar: Grammar,
group: &[&CapturedTokenTree],
nodes: &[String],
) -> Result<LinkRow, NetworkCaptureError> {
let [_link, name_tree, assigned_by, from_tree, to_word, to_tree] = group else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group)));
};
if assigned_by.punct() != Some('=') || to_word.word() != Some("to") {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group)));
}
let (Some(name), Some(from), Some(to)) = (name_tree.word(), from_tree.word(), to_tree.word())
else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group)));
};
if !nodes.iter().any(|held| held == from) {
return Err(refused(
grammar,
CaptureCause::EndpointUnknown,
from_tree.span(),
));
}
if !nodes.iter().any(|held| held == to) {
return Err(refused(
grammar,
CaptureCause::EndpointUnknown,
to_tree.span(),
));
}
Ok(LinkRow::drawn(
name.to_owned(),
from.to_owned(),
to.to_owned(),
))
}
fn schedule_of(
grammar: Grammar,
group: &[&CapturedTokenTree],
world: &World,
) -> Result<ScheduleRow, NetworkCaptureError> {
let [_schedule, name_tree, assigned_by, roster] = group else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group)));
};
let Some(name) = name_tree.word() else {
return Err(refused(
grammar,
CaptureCause::ClauseUnread,
name_tree.span(),
));
};
if !rendered_identifier(name) {
return Err(refused(
grammar,
CaptureCause::ClauseUnread,
name_tree.span(),
));
}
if rust_keyword(name) || RESERVED.contains(&name) {
return Err(refused(
grammar,
CaptureCause::NameReserved,
name_tree.span(),
));
}
if assigned_by.punct() != Some('=') {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group)));
}
let Some((CapturedDelimiter::Bracket, members)) = roster.group() else {
return Err(refused(grammar, CaptureCause::RosterUnread, roster.span()));
};
let mut disciplines: Vec<DisciplineRow> = Vec::new();
for phrase in &comma_groups(grammar, members, refused)? {
let (link, fault) = phrase_of(grammar, phrase, world)?;
match disciplines
.iter_mut()
.find(|held| held.link().name() == link.name())
{
Some(discipline) => discipline.push(fault),
None => disciplines.push(DisciplineRow::gathered(link, vec![fault])),
}
}
Ok(ScheduleRow::declared(name.to_owned(), disciplines))
}
fn phrase_of(
grammar: Grammar,
phrase: &[&CapturedTokenTree],
world: &World,
) -> Result<(LinkRow, FaultRow), NetworkCaptureError> {
let (link_tree, fault) = match phrase {
[verb, link, at_word, at]
if verb.word() == Some("drop") && at_word.word() == Some("at") =>
{
(
link,
FaultRow::Drop {
at: ordinal_of(grammar, at)?,
},
)
}
[verb, link, at_word, at]
if verb.word() == Some("duplicate") && at_word.word() == Some("at") =>
{
(
link,
FaultRow::Duplicate {
at: ordinal_of(grammar, at)?,
},
)
}
[verb, link, at_word, at, by_word, by]
if verb.word() == Some("delay")
&& at_word.word() == Some("at")
&& by_word.word() == Some("by") =>
{
(
link,
FaultRow::Delay {
at: ordinal_of(grammar, at)?,
by: ordinal_of(grammar, by)?,
},
)
}
[verb, link, from_word, from, until_word, until]
if verb.word() == Some("partition")
&& from_word.word() == Some("from")
&& until_word.word() == Some("until") =>
{
(
link,
FaultRow::Partition {
from: number_of(grammar, from)?,
until: number_of(grammar, until)?,
},
)
}
_unread => {
return Err(refused(
grammar,
CaptureCause::PhraseUnread,
opening(phrase),
));
}
};
let Some(link) = link_tree.word() else {
return Err(refused(
grammar,
CaptureCause::PhraseUnread,
link_tree.span(),
));
};
world
.links
.iter()
.find(|held| held.name() == link)
.cloned()
.map(|resolved| (resolved, fault))
.ok_or_else(|| refused(grammar, CaptureCause::EndpointUnknown, link_tree.span()))
}
fn number_of(grammar: Grammar, tree: &CapturedTokenTree) -> Result<u64, NetworkCaptureError> {
let digits = tree
.number()
.ok_or_else(|| refused(grammar, CaptureCause::PhraseUnread, tree.span()))?;
digits
.parse::<u64>()
.map_err(|_beyond| refused(grammar, CaptureCause::NumberBeyondSeat, tree.span()))
}
fn ordinal_of(grammar: Grammar, tree: &CapturedTokenTree) -> Result<u32, NetworkCaptureError> {
let digits = tree
.number()
.ok_or_else(|| refused(grammar, CaptureCause::PhraseUnread, tree.span()))?;
digits
.parse::<u32>()
.map_err(|_beyond| refused(grammar, CaptureCause::NumberBeyondSeat, tree.span()))
}