oca_file/ocafile/
mod.rs

1pub mod error;
2
3use std::collections::HashMap;
4
5use self::error::ParseError;
6use oca_file_semantics::ocafile::{
7    parse_from_string as semantics_parse_from_string, OCAAst as SemanticsAst,
8};
9use oca_file_transformation::ocafile::{
10    parse_from_string as transformation_parse_from_string, TransformationAST,
11};
12use pest::Parser;
13
14#[derive(pest_derive::Parser)]
15#[grammar = "ocafile.pest"]
16pub struct OCAfileParser;
17
18#[derive(Debug)]
19pub enum OCAAst {
20    TransformationAst(TransformationAST),
21    SemanticsAst(SemanticsAst),
22}
23
24pub type Pair<'a> = pest::iterators::Pair<'a, Rule>;
25
26pub fn parse_from_string(unparsed_file: String) -> Result<OCAAst, ParseError> {
27    let file = OCAfileParser::parse(Rule::file, &unparsed_file)
28        .map_err(|e| {
29            let (line_number, column_number) = match e.line_col {
30                pest::error::LineColLocation::Pos((line, column)) => (line, column),
31                pest::error::LineColLocation::Span((line, column), _) => (line, column),
32            };
33            ParseError::GrammarError {
34                line_number,
35                column_number,
36                raw_line: e.line().to_string(),
37                message: e.variant.to_string(),
38            }
39        })?
40        .next()
41        .unwrap();
42
43    let mut meta = HashMap::new();
44
45    for line in file.into_inner() {
46        if let Rule::EOI = line.as_rule() {
47            continue;
48        }
49        if let Rule::comment = line.as_rule() {
50            continue;
51        }
52        if let Rule::meta_comment = line.as_rule() {
53            let mut key = "".to_string();
54            let mut value = "".to_string();
55            for attr in line.into_inner() {
56                match attr.as_rule() {
57                    Rule::meta_attr_key => {
58                        key = attr.as_str().to_string();
59                    }
60                    Rule::meta_attr_value => {
61                        value = attr.as_str().to_string();
62                    }
63                    _ => {
64                        return Err(ParseError::MetaError(attr.as_str().to_string()));
65                    }
66                }
67            }
68            if key.is_empty() {
69                return Err(ParseError::MetaError("key is empty".to_string()));
70            }
71            if value.is_empty() {
72                return Err(ParseError::MetaError("value is empty".to_string()));
73            }
74            meta.insert(key, value);
75            continue;
76        }
77        if let Rule::commands = line.as_rule() {
78            continue;
79        }
80        if let Rule::empty_line = line.as_rule() {
81            continue;
82        }
83    }
84
85    if let Some(value) = meta.get("precompiler") {
86        if value == "transformation" {
87            return Ok(OCAAst::TransformationAst(
88                transformation_parse_from_string(unparsed_file)
89                    .map_err(ParseError::TransformationError)?,
90            ));
91        } else if value == "semantics" {
92            return Ok(OCAAst::SemanticsAst(
93                semantics_parse_from_string(unparsed_file).map_err(ParseError::SemanticsError)?,
94            ));
95        } else {
96            return Err(ParseError::MetaError("unknown precompiler".to_string()));
97        }
98    }
99
100    Ok(OCAAst::SemanticsAst(
101        semantics_parse_from_string(unparsed_file).map_err(ParseError::SemanticsError)?,
102    ))
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn parse_transformation_from_string_valid() {
111        let _ = env_logger::builder().is_test(true).try_init();
112
113        let unparsed_file = r#"
114-- precompiler=transformation
115-- version=0.0.1
116-- name=Objekt
117RENAME ATTRIBUTE surname=last_name
118"#;
119        let oca_ast = parse_from_string(unparsed_file.to_string()).unwrap();
120        assert!(matches!(oca_ast, OCAAst::TransformationAst(_)));
121    }
122
123    #[test]
124    fn parse_semantics_from_string_valid() {
125        let _ = env_logger::builder().is_test(true).try_init();
126
127        let unparsed_file = r#"
128-- precompiler=semantics
129-- version=0.0.1
130-- name=Objekt
131ADD ATTRIBUTE surname=Text
132"#;
133        let oca_ast = parse_from_string(unparsed_file.to_string()).unwrap();
134        assert!(matches!(oca_ast, OCAAst::SemanticsAst(_)));
135    }
136
137    #[test]
138    fn parse_semantics_from_string_by_default() {
139        let _ = env_logger::builder().is_test(true).try_init();
140
141        let unparsed_file = r#"
142-- version=0.0.1
143-- name=Objekt
144ADD ATTRIBUTE surname=Text
145"#;
146        let oca_ast = parse_from_string(unparsed_file.to_string()).unwrap();
147        assert!(matches!(oca_ast, OCAAst::SemanticsAst(_)));
148    }
149}