1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
//! Abstract Syntax Tree (AST) of EXPRESS Language

mod algorithm;
mod entity;
mod expression;
mod schema;
mod types;

pub use algorithm::*;
pub use entity::*;
pub use expression::*;
pub use schema::*;
pub use types::*;

use crate::parser::{combinator::*, *};
use nom::Finish;

/// Remarks in EXPRESS input, `(* ... *)` or `-- ...`
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Remark {
    pub tag: Option<Vec<String>>,
    pub remark: String,
}

/// Entire syntax tree parsed from EXPRESS Language string
#[derive(Debug, Clone, PartialEq)]
pub struct SyntaxTree {
    pub schemas: Vec<Schema>,
    pub remarks: Vec<Remark>,
}

impl SyntaxTree {
    pub fn parse(input: &str) -> Result<Self, nom::error::VerboseError<&str>> {
        let (residual, (schemas, remarks)) = tuple((spaces, many1(schema_decl), spaces))
            .map(|(_start_space, schemas, _end_space)| schemas)
            .parse(input)
            .finish()?;
        assert!(residual.is_empty());
        Ok(SyntaxTree { schemas, remarks })
    }

    // Example syntax tree for easy testing
    //
    // FIXME Replace by e.g. proptest
    // https://github.com/AltSysrq/proptest
    #[allow(dead_code)]
    pub(crate) fn example() -> Self {
        Self::parse(
            r#"
            SCHEMA one;
              ENTITY first;
                m_ref : second;
                fattr : STRING;
              END_ENTITY;
              ENTITY second;
                sattr : STRING;
              END_ENTITY;
            END_SCHEMA;

            SCHEMA geometry0;
              ENTITY point;
                x, y, z: REAL;
              END_ENTITY;
            END_SCHEMA;
            "#
            .trim(),
        )
        .unwrap()
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn parse_remarks() {
        let st = super::SyntaxTree::parse(
            r#"
            SCHEMA one;
              ENTITY first;
                m_ref : second;
                fattr : STRING;
              END_ENTITY; -- first
              ENTITY second;
                sattr : STRING;
              END_ENTITY; -- second
              (* this is the example! *)
            END_SCHEMA;

            (* Hey! *)

            SCHEMA geometry0;
              ENTITY point;
                x, (* y, *) z: REAL; -- skip y
              END_ENTITY;
            END_SCHEMA;
            "#,
        )
        .unwrap();
        dbg!(&st);
        assert_eq!(st.remarks.len(), 6);
    }
}