rasn-compiler 0.1.0

An ASN.1 compiler producing bindings for the rasn framework
Documentation
use crate::intermediate::*;
use nom::{
    branch::alt,
    bytes::complete::tag,
    character::complete::char,
    combinator::{into, map, opt, recognize, value},
    multi::{separated_list1, many0},
    sequence::{delimited, pair, preceded, terminated, tuple},
    IResult,
};

use super::{
    common::{identifier, skip_ws, skip_ws_and_comments, value_identifier},
    object_identifier::object_identifier_value,
};

pub fn module_reference<'a>(input: &'a str) -> IResult<&'a str, ModuleReference> {
    skip_ws_and_comments(into(tuple((
        identifier,
        opt(skip_ws(definitive_identification)),
        skip_ws_and_comments(delimited(
            tag(DEFINITIONS),
            opt(environments),
            skip_ws_and_comments(pair(tag(ASSIGN), skip_ws_and_comments(tag(BEGIN)))),
        )),
        opt(exports),
        opt(imports),
    ))))(input)
}

fn definitive_identification<'a>(input: &'a str) -> IResult<&'a str, DefinitiveIdentifier> {
    into(pair(object_identifier_value, opt(iri_value)))(input)
}

fn iri_value<'a>(input: &'a str) -> IResult<&'a str, &'a str> {
    skip_ws_and_comments(delimited(
        tag("\"/"),
        recognize(separated_list1(char('/'), identifier)),
        char('"'),
    ))(input)
}

fn exports<'a>(input: &'a str) -> IResult<&'a str, Exports> {
    skip_ws_and_comments(delimited(
        tag(EXPORTS),
        skip_ws(alt((
            value(Exports::All, tag(ALL)),
            into(separated_list1(
                skip_ws(char(COMMA)),
                skip_ws(alt((parameterized_identifier, identifier))),
            )),
        ))),
        char(SEMICOLON),
    ))(input)
}

fn imports<'a>(input: &'a str) -> IResult<&'a str, Vec<Import>> {
    skip_ws_and_comments(delimited(
        tag(IMPORTS),
        skip_ws_and_comments(many0(import)),
        skip_ws_and_comments(char(SEMICOLON)),
    ))(input)
}

fn parameterized_identifier<'a>(input: &'a str) -> IResult<&'a str, &'a str> {
    terminated(identifier, tag("{}"))(input)
}

fn import<'a>(input: &'a str) -> IResult<&'a str, Import> {
    into(skip_ws_and_comments(pair(
        separated_list1(
            skip_ws(char(COMMA)),
            skip_ws(alt((parameterized_identifier, identifier))),
        ),
        preceded(
            skip_ws_and_comments(tag(FROM)),
            skip_ws_and_comments(tuple((
                identifier,
                alt((
                    value(None, skip_ws_and_comments(value_identifier)),
                    opt(skip_ws_and_comments(object_identifier_value)),
                )),
                opt(skip_ws_and_comments(alt((
                    tag(WITH_SUCCESSORS),
                    tag(WITH_DESCENDANTS),
                )))),
            ))),
        ),
    )))(input)
}

fn environments<'a>(
    input: &'a str,
) -> IResult<
    &'a str,
    (
        Option<EncodingReferenceDefault>,
        TaggingEnvironment,
        ExtensibilityEnvironment,
    ),
> {
    tuple((
        opt(skip_ws_and_comments(into(terminated(
            identifier,
            skip_ws(tag(INSTRUCTIONS)),
        )))),
        skip_ws_and_comments(terminated(
            map(
                alt((tag(AUTOMATIC), tag(IMPLICIT), tag(EXPLICIT))),
                |m| match m {
                    AUTOMATIC => TaggingEnvironment::Automatic,
                    IMPLICIT => TaggingEnvironment::Implicit,
                    _ => TaggingEnvironment::Explicit,
                },
            ),
            skip_ws(tag(TAGS)),
        )),
        skip_ws_and_comments(map(opt(tag(EXTENSIBILITY_IMPLIED)), |m| {
            if m.is_some() {
                ExtensibilityEnvironment::Implied
            } else {
                ExtensibilityEnvironment::Explicit
            }
        })),
    ))(input)
}

#[cfg(test)]
mod tests {
    use std::vec;

    use crate::parser::module_reference::*;

    #[test]
    fn parses_a_module_reference() {
        assert_eq!(module_reference(r#"--! @options: no-fields-header

    ETSI-ITS-CDD {itu-t (0) identified-organization (4) etsi (0) itsDomain (5) wg1 (1) 102894 cdd (2) major-version-3 (3) minor-version-1 (1)}
    
    DEFINITIONS AUTOMATIC TAGS ::=
    
    BEGIN
    "#).unwrap().1,
    ModuleReference {name:"ETSI-ITS-CDD".into(),module_identifier:Some(DefinitiveIdentifier::DefinitiveOID(ObjectIdentifierValue(vec![ObjectIdentifierArc{name:Some("itu-t".into()),number:Some(0)},ObjectIdentifierArc{name:Some("identified-organization".into()),number:Some(4)},ObjectIdentifierArc{name:Some("etsi".into()),number:Some(0)},ObjectIdentifierArc{name:Some("itsDomain".into()),number:Some(5)},ObjectIdentifierArc{name:Some("wg1".into()),number:Some(1)},ObjectIdentifierArc{name:None,number:Some(102894)},ObjectIdentifierArc{name:Some("cdd".into()),number:Some(2)},ObjectIdentifierArc{name:Some("major-version-3".into()),number:Some(3)},ObjectIdentifierArc{name:Some("minor-version-1".into()),number:Some(1)}]))),encoding_reference_default:None,tagging_environment:crate::intermediate::TaggingEnvironment::Automatic,extensibility_environment:crate::intermediate::ExtensibilityEnvironment::Explicit, imports: vec![], exports: None }
  )
    }

    #[test]
    fn parses_a_module_reference_with_imports() {
        assert_eq!(module_reference(r#"CPM-PDU-Descriptions { itu-t (0) identified-organization (4) etsi (0) itsDomain (5) wg1 (1) ts (103324) cpm (1) major-version-1 (1) minor-version-1(1)}

        DEFINITIONS AUTOMATIC TAGS ::=
        
        BEGIN
        
        IMPORTS
        
        ItsPduHeader, MessageRateHz, MessageSegmentationInfo, OrdinalNumber1B,  ReferencePosition, StationType, TimestampIts
        FROM ETSI-ITS-CDD {itu-t (0) identified-organization (4) etsi (0) itsDomain (5) wg1 (1) ts (102894) cdd (2) major-version-3 (3) minor-version-1 (1)}
        WITH SUCCESSORS
        
        OriginatingRsuContainer, OriginatingVehicleContainer
        FROM CPM-OriginatingStationContainers {itu-t (0) identified-organization (4) etsi (0) itsDomain (5) wg1 (1) ts (103324) originatingStationContainers (2) major-version-1 (1) minor-version-1(1)}
        WITH SUCCESSORS;        
    "#).unwrap().1,
    ModuleReference { name: "CPM-PDU-Descriptions".into(), module_identifier: Some(DefinitiveIdentifier::DefinitiveOID(ObjectIdentifierValue(vec![ObjectIdentifierArc { name: Some("itu-t".into()), number: Some(0) }, ObjectIdentifierArc { name: Some("identified-organization".into()), number: Some(4) }, ObjectIdentifierArc { name: Some("etsi".into()), number: Some(0) }, ObjectIdentifierArc { name: Some("itsDomain".into()), number: Some(5) }, ObjectIdentifierArc { name: Some("wg1".into()), number: Some(1) }, ObjectIdentifierArc { name: Some("ts".into()), number: Some(103324) }, ObjectIdentifierArc { name: Some("cpm".into()), number: Some(1) }, ObjectIdentifierArc { name: Some("major-version-1".into()), number: Some(1) }, ObjectIdentifierArc { name: Some("minor-version-1".into()), number: Some(1) }]))), encoding_reference_default: None, tagging_environment: TaggingEnvironment::Automatic, extensibility_environment: ExtensibilityEnvironment::Explicit, imports: vec![Import { types: vec!["ItsPduHeader".into(), "MessageRateHz".into(), "MessageSegmentationInfo".into(), "OrdinalNumber1B".into(), "ReferencePosition".into(), "StationType".into(), "TimestampIts".into()], origin_name: "ETSI-ITS-CDD".into(), origin_identifier: Some(ObjectIdentifierValue(vec![ObjectIdentifierArc { name: Some("itu-t".into()), number: Some(0) }, ObjectIdentifierArc { name: Some("identified-organization".into()), number: Some(4) }, ObjectIdentifierArc { name: Some("etsi".into()), number: Some(0) }, ObjectIdentifierArc { name: Some("itsDomain".into()), number: Some(5) }, ObjectIdentifierArc { name: Some("wg1".into()), number: Some(1) }, ObjectIdentifierArc { name: Some("ts".into()), number: Some(102894) }, ObjectIdentifierArc { name: Some("cdd".into()), number: Some(2) }, ObjectIdentifierArc { name: Some("major-version-3".into()), number: Some(3) }, ObjectIdentifierArc { name: Some("minor-version-1".into()), number: Some(1) }])), with: Some(With::Successors) }, Import { types: vec!["OriginatingRsuContainer".into(), "OriginatingVehicleContainer".into()], origin_name: "CPM-OriginatingStationContainers".into(), origin_identifier: Some(ObjectIdentifierValue(vec![ObjectIdentifierArc { name: Some("itu-t".into()), number: Some(0) }, ObjectIdentifierArc { name: Some("identified-organization".into()), number: Some(4) }, ObjectIdentifierArc { name: Some("etsi".into()), number: Some(0) }, ObjectIdentifierArc { name: Some("itsDomain".into()), number: Some(5) }, ObjectIdentifierArc { name: Some("wg1".into()), number: Some(1) }, ObjectIdentifierArc { name: Some("ts".into()), number: Some(103324) }, ObjectIdentifierArc { name: Some("originatingStationContainers".into()), number: Some(2) }, ObjectIdentifierArc { name: Some("major-version-1".into()), number: Some(1) }, ObjectIdentifierArc { name: Some("minor-version-1".into()), number: Some(1) }])), with: Some(With::Successors) }], exports: None } )
    }

    #[test]
    fn parses_iri_value() {
        assert_eq!(module_reference(r#"CMSCKMKeyManagement {itu-t recommendation(0) x(24) cms-profile(894) module(0) cKMKeyManagement(1) version1(1)}
        "/ITU-T/Recommendation/X/CMS-Profile/Module/CKMKeyManagement/Version1"
        DEFINITIONS ::=
        BEGIN
        EXPORTS ALL;
        IMPORTS 
        ALGORITHM,AlgorithmIdentifier{}
        FROM AlgorithmInformation-2009
        {iso(1) identified-organization(3) dod(6) internet(1) security(5)
        mechanisms(5) pkix(7) id-mod(0) id-mod-algorithmInformation-02(58)} WITH DESCENDANTS;"#).unwrap().1,
        ModuleReference { 
            name: "CMSCKMKeyManagement".into(), 
            module_identifier: Some(DefinitiveIdentifier::DefinitiveOIDandIRI { 
                oid: ObjectIdentifierValue(vec![
                    ObjectIdentifierArc { name: Some("itu-t".into()), number: None },
                    ObjectIdentifierArc { name: Some("recommendation".into()), number: Some(0) },
                    ObjectIdentifierArc { name: Some("x".into()), number: Some(24) },
                    ObjectIdentifierArc { name: Some("cms-profile".into()), number: Some(894) },
                    ObjectIdentifierArc { name: Some("module".into()), number: Some(0) },
                    ObjectIdentifierArc { name: Some("cKMKeyManagement".into()), number: Some(1) },
                    ObjectIdentifierArc { name: Some("version1".into()), number: Some(1) },
                ]), 
                iri: "ITU-T/Recommendation/X/CMS-Profile/Module/CKMKeyManagement/Version1".into()
            }), 
            encoding_reference_default: None, 
            tagging_environment: TaggingEnvironment::Explicit, 
            extensibility_environment: ExtensibilityEnvironment::Explicit, 
            imports: vec![
                Import { 
                    types: vec!["ALGORITHM".into(), "AlgorithmIdentifier".into()], 
                    origin_name: "AlgorithmInformation-2009".into(), 
                    origin_identifier: Some(ObjectIdentifierValue(vec![
                        ObjectIdentifierArc { name: Some("iso".into()), number: Some(1) },
                        ObjectIdentifierArc { name: Some("identified-organization".into()), number: Some(3) },
                        ObjectIdentifierArc { name: Some("dod".into()), number: Some(6) },
                        ObjectIdentifierArc { name: Some("internet".into()), number: Some(1) },
                        ObjectIdentifierArc { name: Some("security".into()), number: Some(5) },
                        ObjectIdentifierArc { name: Some("mechanisms".into()), number: Some(5) },
                        ObjectIdentifierArc { name: Some("pkix".into()), number: Some(7) },
                        ObjectIdentifierArc { name: Some("id-mod".into()), number: Some(0) },
                        ObjectIdentifierArc { name: Some("id-mod-algorithmInformation-02".into()), number: Some(58) },
                    ])), 
                    with: Some(With::Descendants) }
            ], 
            exports: Some(Exports::All)
        })
    }

    #[test]
    fn parses_imports() {
        assert_eq!(
            imports(
                r#"IMPORTS 
            DomainParameters
            FROM ANSI-X9-42
            {iso(1) member-body(2) us(840) ansi-x942(10046) module(5) 1}
            ECDomainParameters
            FROM ANSI-X9-62
            {iso(1) member-body(2) us(840) 10045 modules(0) 2};"#
            )
            .unwrap()
            .1,
            vec![
                Import {
                    types: vec!["DomainParameters".into()],
                    origin_name: "ANSI-X9-42".into(),
                    origin_identifier: Some(ObjectIdentifierValue(vec![
                        ObjectIdentifierArc {
                            name: Some("iso".into()),
                            number: Some(1)
                        },
                        ObjectIdentifierArc {
                            name: Some("member-body".into()),
                            number: Some(2)
                        },
                        ObjectIdentifierArc {
                            name: Some("us".into()),
                            number: Some(840)
                        },
                        ObjectIdentifierArc {
                            name: Some("ansi-x942".into()),
                            number: Some(10046)
                        },
                        ObjectIdentifierArc {
                            name: Some("module".into()),
                            number: Some(5)
                        },
                        ObjectIdentifierArc {
                            name: None,
                            number: Some(1)
                        },
                    ])),
                    with: None
                },
                Import {
                    types: vec!["ECDomainParameters".into()],
                    origin_name: "ANSI-X9-62".into(),
                    origin_identifier: Some(ObjectIdentifierValue(vec![
                        ObjectIdentifierArc {
                            name: Some("iso".into()),
                            number: Some(1)
                        },
                        ObjectIdentifierArc {
                            name: Some("member-body".into()),
                            number: Some(2)
                        },
                        ObjectIdentifierArc {
                            name: Some("us".into()),
                            number: Some(840)
                        },
                        ObjectIdentifierArc {
                            name: None,
                            number: Some(10045)
                        },
                        ObjectIdentifierArc {
                            name: Some("modules".into()),
                            number: Some(0)
                        },
                        ObjectIdentifierArc {
                            name: None,
                            number: Some(2)
                        },
                    ])),
                    with: None
                }
            ]
        )
    }
}