sysml_parser/
grammar.rs

1// This is free and unencumbered software released into the public domain.
2
3use super::lexer::*;
4use crate::{
5    prelude::{vec, String, Vec},
6    ParsedAttribute, ParsedBlock, ParsedImport, ParsedMember, ParsedModel, ParsedPackage,
7    ParsedPort, Span, SyntaxResult,
8};
9use nom::{
10    branch::alt,
11    bytes::complete::{tag, take_while},
12    character::complete::{char, multispace0, multispace1},
13    combinator::{map, opt},
14    error::context,
15    multi::many0,
16    sequence::{delimited, preceded, terminated},
17};
18use sysml_model::QualifiedName;
19
20pub fn model<'a>(input: impl Into<Span<'a>>) -> SyntaxResult<'a, (Span<'a>, ParsedModel)> {
21    let (input, package) =
22        context("model", delimited(multispace0, package, multispace0))(input.into())?;
23
24    Ok((
25        input,
26        ParsedModel {
27            members: vec![ParsedMember::Package(package)],
28        },
29    ))
30}
31
32pub fn members<'a>(input: impl Into<Span<'a>>) -> SyntaxResult<'a, (Span<'a>, Vec<ParsedMember>)> {
33    let (input, members) = context(
34        "tokens",
35        many0(alt((
36            terminated(map(import, ParsedMember::Import), multispace0),
37            terminated(map(package, ParsedMember::Package), multispace0),
38            terminated(map(block_usage, ParsedMember::BlockUsage), multispace0),
39            terminated(
40                map(attribute_usage, ParsedMember::AttributeUsage),
41                multispace0,
42            ),
43            terminated(map(port_usage, ParsedMember::PortUsage), multispace0),
44        ))),
45    )(input.into())?;
46
47    Ok((input, members))
48}
49
50pub fn import<'a>(input: impl Into<Span<'a>>) -> SyntaxResult<'a, (Span<'a>, ParsedImport)> {
51    use nom::AsChar;
52    let (input, _) = tag("import")(input.into())?;
53    let (input, _) = multispace1(input)?;
54    let (input, name) = map(take_while(|c| AsChar::as_char(c) != ';'), |span: Span| {
55        QualifiedName::from(span.into_fragment())
56    })(input)?;
57    let (input, _) = char(';')(input)?;
58
59    Ok((input, ParsedImport::new(name)))
60}
61
62pub fn package<'a>(input: impl Into<Span<'a>>) -> SyntaxResult<'a, (Span<'a>, ParsedPackage)> {
63    let (input, (name, short_name, _, members)) = element(input.into(), "package")?;
64
65    Ok((
66        input,
67        ParsedPackage {
68            name: name.map(String::from),
69            short_name: short_name.map(String::from),
70            members,
71        },
72    ))
73}
74
75pub fn block_usage<'a>(input: impl Into<Span<'a>>) -> SyntaxResult<'a, (Span<'a>, ParsedBlock)> {
76    let (input, (name, short_name, definition, members)) = element(input.into(), "block")?;
77
78    Ok((
79        input,
80        ParsedBlock {
81            name,
82            short_name,
83            definition,
84            members,
85        },
86    ))
87}
88
89pub fn attribute_usage<'a>(
90    input: impl Into<Span<'a>>,
91) -> SyntaxResult<'a, (Span<'a>, ParsedAttribute)> {
92    let (input, (name, short_name, definition, members)) = element(input.into(), "attribute")?;
93
94    Ok((
95        input,
96        ParsedAttribute {
97            name,
98            short_name,
99            definition,
100            members,
101        },
102    ))
103}
104
105pub fn port_usage<'a>(input: impl Into<Span<'a>>) -> SyntaxResult<'a, (Span<'a>, ParsedPort)> {
106    let (input, (name, short_name, definition, members)) = element(input.into(), "port")?;
107
108    Ok((
109        input,
110        ParsedPort {
111            name,
112            short_name,
113            definition,
114            members,
115        },
116    ))
117}
118
119pub fn element<'a>(
120    input: impl Into<Span<'a>>,
121    tag_name: &'static str,
122) -> SyntaxResult<
123    'a,
124    (
125        Span<'a>,
126        (
127            Option<String>,
128            Option<String>,
129            Option<QualifiedName>,
130            Vec<ParsedMember>,
131        ),
132    ),
133> {
134    let (input, _) = context(tag_name, tag(tag_name))(input.into())?;
135    let (input, _) = multispace1(input)?;
136    let (input, (name, short_name)) = identification(input)?;
137    let (input, _) = multispace0(input)?;
138    let (input, definition) = opt(delimited(
139        terminated(char(':'), multispace0),
140        qualified_name,
141        multispace0,
142    ))(input)?;
143    let (input, members) = alt((
144        map(char(';'), |_| Vec::new()),
145        delimited(
146            char('{'),
147            preceded(multispace0, members),
148            preceded(multispace0, char('}')),
149        ),
150    ))(input)?;
151
152    Ok((input, (name, short_name, definition, members)))
153}
154
155pub fn identification<'a>(
156    input: impl Into<Span<'a>>,
157) -> SyntaxResult<'a, (Span<'a>, (Option<String>, Option<String>))> {
158    let (input, short_name) = opt(delimited(char('<'), name, char('>')))(input.into())?;
159    let (input, _) = multispace0(input)?;
160    let (input, name) = opt(name)(input)?;
161
162    Ok((input, (name, short_name)))
163}
164
165#[cfg(test)]
166mod tests {}