sysml-v2-parser 0.14.0

SysML v2 textual notation parser for Rust
Documentation
//! Occurrence definition and usage parsing.

use crate::ast::{
    AssertConstraintMember, ConstraintDefBody, DefinitionBody, Node, OccurrenceBodyElement,
    OccurrenceDef, OccurrenceUsage, OccurrenceUsageBody, ParseErrorNode,
};
use crate::parser::attribute::attribute_usage;
use crate::parser::build_recovery_error_node_from_span;
use crate::parser::constraint::{structured_constraint_body, StructuredConstraintBody};
use crate::parser::lex::{
    name, qualified_name, recover_body_element, redefine_operator,
    skip_until_brace_end, subset_operator, ws1, ws_and_comments,
};
use crate::parser::metadata_annotation::annotation;
use crate::parser::definition_prefix::{parse_definition_prefix, DefinitionPrefixOptions};
use crate::parser::node_from_to;
use crate::parser::part::part_usage;
use crate::parser::requirement::doc_comment;
use crate::parser::Input;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::{map, opt};
use nom::sequence::preceded;
use nom::IResult;
use nom::Parser;

const OCCURRENCE_BODY_STARTERS: &[&[u8]] = &[
    b"doc",
    b"assert",
    b"attribute",
    b"part",
    b"individual",
    b"occurrence",
    b"snapshot",
    b"timeslice",
    b"@",
    b"#",
];

fn definition_body(input: Input<'_>) -> IResult<Input<'_>, DefinitionBody> {
    let (input, _) = ws_and_comments(input)?;
    alt((
        map(tag(&b";"[..]), |_| DefinitionBody::Semicolon),
        map(
            nom::sequence::delimited(
                tag(&b"{"[..]),
                skip_until_brace_end,
                preceded(ws_and_comments, tag(&b"}"[..])),
            ),
            |_| DefinitionBody::Brace,
        ),
    ))
    .parse(input)
}

pub(crate) fn occurrence_def(input: Input<'_>) -> IResult<Input<'_>, Node<OccurrenceDef>> {
    let start = input;
    let (input, prefix) = parse_definition_prefix(
        input,
        DefinitionPrefixOptions::new(b"occurrence").def_required(),
    )?;
    let (input, body) = definition_body(input)?;
    Ok((
        input,
        node_from_to(
            start,
            input,
            OccurrenceDef {
                is_abstract: prefix.is_abstract,
                identification: prefix.identification,
                specializes: prefix.specializes,
                specializes_span: prefix.specializes_span,
                body,
            },
        ),
    ))
}

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, subsets) = opt(preceded(
        preceded(ws_and_comments, subset_operator),
        preceded(ws_and_comments, qualified_name),
    ))
    .parse(input)?;
    let (input, redefines) = opt(preceded(
        preceded(ws_and_comments, redefine_operator),
        preceded(ws_and_comments, qualified_name),
    ))
    .parse(input)?;
    let (input, type_name) = opt(preceded(
        preceded(ws_and_comments, tag(&b":"[..])),
        preceded(ws_and_comments, qualified_name),
    ))
    .parse(input)?;
    let (input, trailing_subsets) = opt(preceded(
        preceded(ws_and_comments, subset_operator),
        preceded(ws_and_comments, qualified_name),
    ))
    .parse(input)?;
    let (input, trailing_redefines) = opt(preceded(
        preceded(ws_and_comments, redefine_operator),
        preceded(ws_and_comments, qualified_name),
    ))
    .parse(input)?;
    let (input, body) = occurrence_usage_body(input)?;
    let (input, post_body_subsets) = opt(preceded(
        preceded(ws_and_comments, subset_operator),
        preceded(ws_and_comments, qualified_name),
    ))
    .parse(input)?;
    let (input, post_body_redefines) = opt(preceded(
        preceded(ws_and_comments, redefine_operator),
        preceded(ws_and_comments, qualified_name),
    ))
    .parse(input)?;
    let has_post_body_modifier = post_body_subsets.is_some() || post_body_redefines.is_some();
    let subsets = subsets.or(trailing_subsets).or(post_body_subsets);
    let redefines = redefines.or(trailing_redefines).or(post_body_redefines);
    let input = if has_post_body_modifier {
        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,
                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, _) = skip_until_brace_end(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;
            }
        }
    }
}

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 }),
    ))
}