use super::render::RESERVED;
use super::{
DisciplineRow, FaultRow, LinkRow, NetworkCaptureError, NetworkDeclaration, ScheduleRow,
};
use crate::descriptor::DirectBinding;
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())?;
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 comma_groups(
grammar: Grammar,
trees: &[CapturedTokenTree],
) -> Result<Vec<Vec<&CapturedTokenTree>>, NetworkCaptureError> {
let mut groups: Vec<Vec<&CapturedTokenTree>> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in trees {
if tree.punct() == Some(',') {
if group.is_empty() {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
}
groups.push(core::mem::take(&mut group));
} else {
group.push(tree);
}
}
if !group.is_empty() {
groups.push(group);
}
Ok(groups)
}
fn head_word<'trees>(group: &[&'trees CapturedTokenTree]) -> Option<&'trees str> {
group.first().and_then(|tree| tree.word())
}
fn opening(group: &[&CapturedTokenTree]) -> SpanHandle {
group.first().map_or(SpanHandle::at(0), |tree| tree.span())
}
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") => read_binding(grammar, group, &mut harness)?,
Some("module") => assigned_once(grammar, group, &mut module, assigned_ident)?,
Some("namespace") => assigned_once(grammar, group, &mut namespace, assigned_text)?,
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_binding(
grammar: Grammar,
group: &[&CapturedTokenTree],
seat: &mut Option<DirectBinding>,
) -> Result<(), NetworkCaptureError> {
if seat.is_some() {
return Err(refused(
grammar,
CaptureCause::ClauseDoubled,
opening(group),
));
}
let value = value_of(group);
let binding = crate::descriptor::binding::direct_binding(value)
.map_err(|(issue, at)| NetworkCaptureError::binding_refused(grammar, issue, at))?;
*seat = Some(binding);
Ok(())
}
fn assigned_once(
grammar: Grammar,
group: &[&CapturedTokenTree],
seat: &mut Option<String>,
read: fn(Grammar, &[&CapturedTokenTree]) -> Result<String, NetworkCaptureError>,
) -> Result<(), NetworkCaptureError> {
if seat.is_some() {
return Err(refused(
grammar,
CaptureCause::ClauseDoubled,
opening(group),
));
}
*seat = Some(read(grammar, group)?);
Ok(())
}
fn assigned_ident(
grammar: Grammar,
group: &[&CapturedTokenTree],
) -> Result<String, NetworkCaptureError> {
let [value] = value_of(group) else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group)));
};
let word = value
.word()
.ok_or_else(|| refused(grammar, CaptureCause::ClauseUnread, value.span()))?;
if !rendered_identifier(word) {
return Err(refused(grammar, CaptureCause::ClauseUnread, value.span()));
}
if rust_keyword(word) {
return Err(refused(grammar, CaptureCause::NameReserved, value.span()));
}
Ok(word.to_owned())
}
fn assigned_text(
grammar: Grammar,
group: &[&CapturedTokenTree],
) -> Result<String, NetworkCaptureError> {
let [value] = value_of(group) else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening(group)));
};
value
.text()
.map(str::to_owned)
.ok_or_else(|| refused(grammar, CaptureCause::ClauseUnread, value.span()))
}
fn value_of<'group, 'trees>(
group: &'group [&'trees CapturedTokenTree],
) -> &'group [&'trees CapturedTokenTree] {
match group {
[_key, assigned_by, value @ ..] if assigned_by.punct() == Some('=') => value,
_malformed => &[],
}
}
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)? {
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()))
}