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}