use crate::ast::{
AssertConstraintMember, ConstraintDefBody, DefinitionBody, DefinitionBodyElement, Node,
OccurrenceBodyElement, OccurrenceUsage, OccurrenceUsageBody, ParseErrorNode,
};
use crate::parser::attribute::attribute_usage;
use crate::parser::body::parse_structured_brace_members;
use crate::parser::build_recovery_error_node_from_span;
use crate::parser::constraint::{structured_constraint_body, StructuredConstraintBody};
use crate::parser::lex::{name, recover_body_element, ws1, ws_and_comments};
use crate::parser::metadata_annotation::annotation;
use crate::parser::node_from_to;
use crate::parser::part::part_usage;
use crate::parser::requirement::doc_comment;
use crate::parser::usage::{optional_typings, specialization_clauses};
use crate::parser::Input;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::sequence::preceded;
use nom::IResult;
use nom::Parser;
pub(crate) const OCCURRENCE_BODY_STARTERS: &[&[u8]] = &[
b"doc",
b"assert",
b"attribute",
b"part",
b"individual",
b"occurrence",
b"snapshot",
b"timeslice",
b"@",
b"#",
];
pub(crate) fn occurrence_definition_body(input: Input<'_>) -> IResult<Input<'_>, DefinitionBody> {
occurrence_definition_body_with_labels(
input,
"definition body",
"recovered_definition_body_element",
)
}
pub(crate) fn occurrence_def_definition_body(input: Input<'_>) -> IResult<Input<'_>, DefinitionBody> {
occurrence_definition_body_with_labels(
input,
"occurrence definition body",
"recovered_occurrence_def_body_element",
)
}
fn occurrence_definition_body_with_labels<'a>(
input: Input<'a>,
scope_label: &'static str,
recovery_code: &'static str,
) -> IResult<Input<'a>, DefinitionBody> {
let (input, _) = ws_and_comments(input)?;
if input.fragment().starts_with(b";") {
let (input, _) = tag(&b";"[..]).parse(input)?;
return Ok((input, DefinitionBody::Semicolon));
}
let (input, elements) = parse_structured_brace_members(
input,
OCCURRENCE_BODY_STARTERS,
scope_label,
recovery_code,
|input| {
let start = input;
let (input, node) = occurrence_body_element(input)?;
Ok((
input,
node_from_to(start, input, DefinitionBodyElement::OccurrenceMember(node)),
))
},
|start, end| {
let recovery = build_recovery_error_node_from_span(
start,
end,
OCCURRENCE_BODY_STARTERS,
scope_label,
recovery_code,
);
node_from_to(
start,
end,
DefinitionBodyElement::Error(node_from_to(start, end, recovery)),
)
},
)?;
Ok((input, DefinitionBody::Brace { elements }))
}
pub(crate) fn occurrence_usage(input: Input<'_>) -> IResult<Input<'_>, Node<OccurrenceUsage>> {
occurrence_usage_with_modifiers(input, false, false, None)
}
pub(crate) fn individual_usage(input: Input<'_>) -> IResult<Input<'_>, Node<OccurrenceUsage>> {
let (input, _) = preceded(ws_and_comments, tag(&b"individual"[..])).parse(input)?;
let (input, _) = ws1(input)?;
occurrence_usage_tail(input, true, false, None)
}
pub(crate) fn snapshot_usage(input: Input<'_>) -> IResult<Input<'_>, Node<OccurrenceUsage>> {
let (input, _) = preceded(ws_and_comments, tag(&b"snapshot"[..])).parse(input)?;
let (input, _) = ws1(input)?;
occurrence_usage_tail(input, false, false, Some("snapshot".to_string()))
}
pub(crate) fn timeslice_usage(input: Input<'_>) -> IResult<Input<'_>, Node<OccurrenceUsage>> {
let (input, _) = preceded(ws_and_comments, tag(&b"timeslice"[..])).parse(input)?;
let (input, _) = ws1(input)?;
occurrence_usage_tail(input, false, false, Some("timeslice".to_string()))
}
pub(crate) fn then_timeslice_usage(input: Input<'_>) -> IResult<Input<'_>, Node<OccurrenceUsage>> {
let (input, _) = preceded(ws_and_comments, tag(&b"then"[..])).parse(input)?;
let (input, _) = ws1(input)?;
let (input, _) = tag(&b"timeslice"[..]).parse(input)?;
let (input, _) = ws1(input)?;
occurrence_usage_tail(input, false, true, Some("timeslice".to_string()))
}
fn occurrence_usage_with_modifiers(
input: Input<'_>,
is_individual: bool,
is_then: bool,
portion_kind: Option<String>,
) -> IResult<Input<'_>, Node<OccurrenceUsage>> {
let (input, _) = preceded(ws_and_comments, tag(&b"occurrence"[..])).parse(input)?;
let (input, _) = ws1(input)?;
occurrence_usage_tail(input, is_individual, is_then, portion_kind)
}
fn occurrence_usage_tail(
input: Input<'_>,
is_individual: bool,
is_then: bool,
portion_kind: Option<String>,
) -> IResult<Input<'_>, Node<OccurrenceUsage>> {
let start = input;
let (input, name_str) = name(input)?;
let (input, leading_clauses) = specialization_clauses(input)?;
let (input, type_name) = optional_typings(input)?;
let type_name = type_name.map(|(_, name)| name);
let (input, trailing_clauses) = specialization_clauses(input)?;
let (input, body) = occurrence_usage_body(input)?;
let (input, post_body_clauses) = specialization_clauses(input)?;
let subsets = post_body_clauses
.subsets
.map(|(name, _filter)| name)
.or_else(|| trailing_clauses.subsets.map(|(name, _filter)| name))
.or_else(|| leading_clauses.subsets.map(|(name, _filter)| name));
let redefines = post_body_clauses
.redefines
.or(trailing_clauses.redefines)
.or(leading_clauses.redefines);
let references = post_body_clauses
.references
.or(trailing_clauses.references)
.or(leading_clauses.references);
let crosses = post_body_clauses
.crosses
.or(trailing_clauses.crosses)
.or(leading_clauses.crosses);
let input = if post_body_clauses.had_any {
let (input, _) = preceded(ws_and_comments, tag(&b";"[..])).parse(input)?;
input
} else {
input
};
Ok((
input,
node_from_to(
start,
input,
OccurrenceUsage {
is_individual,
is_then,
portion_kind,
name: name_str,
type_name,
subsets,
redefines,
references,
crosses,
body,
},
),
))
}
fn occurrence_usage_body(input: Input<'_>) -> IResult<Input<'_>, OccurrenceUsageBody> {
let (input, _) = ws_and_comments(input)?;
alt((
map(tag(&b";"[..]), |_| OccurrenceUsageBody::Semicolon),
occurrence_usage_body_brace,
))
.parse(input)
}
fn occurrence_usage_body_brace(input: Input<'_>) -> IResult<Input<'_>, OccurrenceUsageBody> {
let (mut input, _) = tag(&b"{"[..]).parse(input)?;
let mut elements = Vec::new();
loop {
let (next, _) = ws_and_comments(input)?;
input = next;
if input.fragment().is_empty() {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Eof,
)));
}
if input.fragment().starts_with(b"}") {
let (input, _) = preceded(ws_and_comments, tag(&b"}"[..])).parse(input)?;
return Ok((input, OccurrenceUsageBody::Brace { elements }));
}
match occurrence_body_element(input) {
Ok((next, element)) => {
if next.location_offset() == input.location_offset() {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Many0,
)));
}
elements.push(element);
input = next;
}
Err(_) => {
let start_unknown = input;
let (next, _) = recover_body_element(input, OCCURRENCE_BODY_STARTERS)?;
if next.location_offset() == start_unknown.location_offset() {
let (input, _) = crate::parser::body::advance_to_closing_brace(input)?;
let (input, _) = preceded(ws_and_comments, tag(&b"}"[..])).parse(input)?;
return Ok((input, OccurrenceUsageBody::Brace { elements }));
}
let recovery = build_recovery_error_node_from_span(
start_unknown,
next,
OCCURRENCE_BODY_STARTERS,
"occurrence body",
"recovered_occurrence_body_element",
);
let node: Node<ParseErrorNode> = node_from_to(start_unknown, next, recovery);
elements.push(node_from_to(
start_unknown,
next,
OccurrenceBodyElement::Error(node),
));
input = next;
}
}
}
}
pub(crate) fn occurrence_body_element(
input: Input<'_>,
) -> IResult<Input<'_>, Node<OccurrenceBodyElement>> {
let (input, _) = ws_and_comments(input)?;
let start = input;
let (input, elem) = alt((
map(doc_comment, OccurrenceBodyElement::Doc),
map(annotation, OccurrenceBodyElement::Annotation),
map(
assert_constraint_member,
OccurrenceBodyElement::AssertConstraint,
),
map(attribute_usage, OccurrenceBodyElement::AttributeUsage),
map(part_usage, |p| {
OccurrenceBodyElement::PartUsage(Box::new(p))
}),
map(individual_usage, |n| {
OccurrenceBodyElement::OccurrenceUsage(Box::new(n))
}),
map(snapshot_usage, |n| {
OccurrenceBodyElement::OccurrenceUsage(Box::new(n))
}),
map(timeslice_usage, |n| {
OccurrenceBodyElement::OccurrenceUsage(Box::new(n))
}),
map(then_timeslice_usage, |n| {
OccurrenceBodyElement::OccurrenceUsage(Box::new(n))
}),
map(occurrence_usage, |n| {
OccurrenceBodyElement::OccurrenceUsage(Box::new(n))
}),
))
.parse(input)?;
Ok((input, node_from_to(start, input, elem)))
}
fn assert_constraint_member(input: Input<'_>) -> IResult<Input<'_>, Node<AssertConstraintMember>> {
let start = input;
let (input, _) = preceded(ws_and_comments, tag(&b"assert"[..])).parse(input)?;
let (input, _) = ws1(input)?;
let (input, _) = tag(&b"constraint"[..]).parse(input)?;
let (input, body) = structured_constraint_body(input)?;
let body = match body {
StructuredConstraintBody::Semicolon => ConstraintDefBody::Semicolon,
StructuredConstraintBody::Brace { elements } => ConstraintDefBody::Brace { elements },
};
Ok((
input,
node_from_to(start, input, AssertConstraintMember { body }),
))
}