rusty_systems/interpretation/abop/
parser.rs1use crate::error::ErrorKind;
38use crate::interpretation::abop::*;
39use crate::parser::parse_prod_string;
40
41type ParsedAbop = (AbopTurtleInterpretation, System, ProductionString);
58
59pub fn parse(string: &str) -> crate::Result<ParsedAbop> {
64 let string = string.trim();
65 if string.is_empty() {
66 return Err(Error::new(ErrorKind::Parse, "String should not be empty"));
67 }
68
69 let mut lines = string.lines().peekable();
70
71 let mut n = 2_usize;
72 let mut delta = 5.0_f32;
73
74 let system = AbopTurtleInterpretation::system()?;
75 let mut prod_count = 0_usize;
76 let mut initial : Option<&str> = None;
77
78 #[allow(clippy::while_let_on_iterator)]
79 while let Some(line) = lines.next() {
80 let line = remove_comment(line);
81 if line.is_empty() {
82 continue;
83 }
84
85 if is_equality_line(line) {
86 let equality = parse_equality(line)?;
87 match equality.name {
88 "n" | "N" => {
89 n = equality.value.parse()?;
90 }
91 "d" | "D" | "delta" | "∂" => {
92 delta = equality.value.parse()?;
93 }
94 _ => return Err(Error::new(ErrorKind::Parse, format!("Unrecognised line {}", line)))
95 }
96
97 continue;
98 }
99
100 if is_initial(line) {
101 initial = Some(parse_initial(line));
102 continue;
103 }
104
105 prod_count += 1;
106 system.parse_production(line)?;
107 }
108
109 if prod_count == 0 {
110 return Err(Error::new(ErrorKind::Parse, "No productions have been supplied"));
111 }
112
113 if initial.is_none() {
114 return Err(Error::new(ErrorKind::Parse, "No initial axiom has been supplied"));
115 }
116
117 let initial = parse_prod_string(initial.unwrap())?;
118
119 let interpretation = AbopTurtleInterpretation::new(n, delta);
120 Ok((interpretation, system, initial))
121}
122
123pub fn parse_file<P: AsRef<std::path::Path>>(name: P) -> crate::Result<ParsedAbop> {
128 let contents = std::fs::read_to_string(name)?;
129 parse(&contents)
130}
131
132
133struct EqualityLine<'a> {
134 pub name: &'a str,
135 pub value: &'a str
136}
137
138fn is_equality_line(line: &str) -> bool {
139 line.contains('=')
140}
141
142fn is_initial(line: &str) -> bool {
143 line.trim().starts_with("initial:")
144}
145
146fn parse_initial(line: &str) -> &str {
147 let parts: Vec<_> = line.splitn(2, ':').collect();
148 parts[1].trim()
149}
150
151fn parse_equality(line: &str) -> crate::Result<EqualityLine> {
152 let parts: Vec<&str> = line.splitn(2, '=').collect();
153 if parts.len() != 2 {
154 return Err(Error::general("Invalid equality line"));
155 }
156 let name = parts[0].trim();
157 let value = parts[1].trim();
158 Ok(EqualityLine { name, value })
159}
160
161fn remove_comment(line: &str) -> &str {
162 line.split('#').next().unwrap().trim()
163}
164
165
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 static GENERAL : &str = "# Totally for testing purposes
172n = 6
173delta = 22.5
174
175initial: X # Here we go
176# Start on a line
177
178Forward -> Forward Forward
179X -> Forward + [ [ X ] - X ] - Forward [ - Forward X ] + X
180
181# ENDED";
182
183
184 #[test]
185 fn test_parsing() {
186
187 let result = parse(GENERAL);
188 assert!(result.is_ok());
189
190 let (_, system, ..) = result.unwrap();
191 assert_eq!(system.production_len(), 2);
192
193 assert_eq!(system.symbol_len(), AbopTurtleInterpretation::system().unwrap().symbol_len());
195
196 }
197}