1use crate::codegen_prelude::AstInfo;
2use crate::parser::peg::parser_sugar_ast::*;
3use crate::parser::syntax_file::ast;
4use crate::parser::syntax_file::ast::{CharacterClassItem, EscapeClosingBracket, SortOrMeta};
5use crate::parser::syntax_file::convert_syntax_file_ast::AstConversionError::{
6 DuplicateStartingRule, NoStartingSort,
7};
8use crate::parser::syntax_file::AST::{DelimitedBound, StringChar};
9use crate::sources::character_class::CharacterClass;
10use std::collections::HashMap;
11use std::num::ParseIntError;
12use thiserror::Error;
13
14#[derive(Debug, Error)]
15pub enum AstConversionError {
16 #[error("grammar contains more than one starting rule")]
17 DuplicateStartingRule,
18
19 #[error("couldn't parse int: {0}")]
20 NumberConversion(ParseIntError),
21
22 #[error("{0} is not a valid annotation")]
23 BadAnnotation(String),
24
25 #[error("no starting sort in syntax file definition")]
26 NoStartingSort,
27}
28
29pub type ConversionResult<T> = Result<T, AstConversionError>;
30
31pub fn convert<M: AstInfo>(inp: ast::AST_ROOT<M>) -> ConversionResult<SyntaxFileAst> {
32 let ast::Program(_, sort_or_metas) = inp;
33 let mut sorts = HashMap::new();
34 let mut start = None;
35
36 for i in sort_or_metas {
37 match i {
38 SortOrMeta::Meta(_, m) => {
39 if start.is_some() {
40 return Err(DuplicateStartingRule);
41 } else {
42 start = Some(convert_identifier(&m.1))
43 }
44 }
45 SortOrMeta::Sort(_, sort) => {
46 let converted = convert_sort(sort)?;
47 sorts.insert(converted.name.clone(), converted);
48 }
49 }
50 }
51
52 Ok(SyntaxFileAst {
53 sorts,
54 starting_sort: start.ok_or(NoStartingSort)?,
55 merges: Default::default(),
56 old_sort_names: vec![],
57 })
58}
59
60fn convert_identifier<M: AstInfo>(inp: &ast::Identifier<M>) -> String {
61 inp.1.trim().to_string()
62}
63
64fn convert_number<M: AstInfo>(inp: ast::Number<M>) -> ConversionResult<u64> {
65 inp.1
66 .parse::<u64>()
67 .map_err(AstConversionError::NumberConversion)
68}
69
70fn convert_escape_closing_bracket<M: AstInfo>(inp: ast::EscapeClosingBracket<M>) -> char {
71 match inp {
72 EscapeClosingBracket::Escaped(_, c) => match c.as_str() {
73 "n" => '\n',
74 "r" => '\r',
75 "t" => '\t',
76 "\\" => '\\',
77 "]" => ']',
78 a => unreachable!("grammar shouldn't allow {} here", a),
79 },
80 EscapeClosingBracket::Unescaped(_, c) => {
81 let c = c.chars().next().expect("only one character");
82 c
83 }
84 }
85}
86
87fn convert_string_char<M>(inp: &ast::StringChar<M>) -> char {
88 match inp {
89 StringChar::Escaped(_, c) => match c.as_str() {
90 "n" => '\n',
91 "r" => '\r',
92 "t" => '\t',
93 "\\" => '\\',
94 "\"" => '"',
95 a => unreachable!("grammar shouldn't allow {} here", a),
96 },
97 StringChar::Normal(_, c) => {
98 let c = c.chars().next().expect("only one character");
99 c
100 }
101 }
102}
103
104fn convert_character_class<M: AstInfo>(
105 inp: ast::CharacterClass<M>,
106) -> ConversionResult<CharacterClass> {
107 let ast::CharacterClass(_, inverted, items) = inp;
108 let mut res = CharacterClass::Nothing;
109
110 for i in items {
111 match i {
112 CharacterClassItem::Range(_, from, to_inclusive) => {
113 res = res.combine(CharacterClass::RangeInclusive {
114 from: convert_escape_closing_bracket(from),
115 to: convert_escape_closing_bracket(to_inclusive),
116 })
117 }
118 CharacterClassItem::SingleChar(_, c) => {
119 let c = convert_escape_closing_bracket(c);
120
121 res = res.combine(CharacterClass::RangeInclusive { from: c, to: c })
122 }
123 }
124 }
125
126 if inverted {
127 res = res.invert();
128 }
129
130 Ok(res)
131}
132
133fn convert_sort<M: AstInfo>(inp: ast::Sort<M>) -> ConversionResult<Sort> {
134 Ok(match inp {
135 ast::Sort::Sort(_, name, annos, constructors) => Sort {
136 documentation: None,
137 name: convert_identifier(&name),
138 constructors: constructors
139 .into_iter()
140 .map(|i| convert_constructor(i))
145 .collect::<Result<_, _>>()?,
146 annotations: annos.map_or(Ok(vec![]), |a| convert_annotations(&a))?,
147 },
148 ast::Sort::SortSingle(_, name, expressions, annotations) => {
149 let name = convert_identifier(&name);
150 Sort {
151 documentation: None,
152 name: name.clone(),
153 constructors: vec![Constructor {
154 documentation: None,
155 name,
156 expression: convert_expressions(expressions)?,
157 annotations: annotations
158 .as_ref()
159 .map_or(Ok(vec![]), |a| convert_annotations(a))?,
160 dont_put_in_ast: false,
161 }],
162 annotations: annotations
163 .as_ref()
164 .map_or(Ok(vec![]), |a| convert_annotations(a))?,
165 }
166 }
167 ast::Sort::SortDocumented(_, comments, sort) => convert_sort(*sort).and_then(|mut i| {
168 i.documentation = Some(convert_comments(comments)?);
169 Ok(i)
170 })?,
171 })
172}
173
174fn convert_comments<M: AstInfo>(inp: Vec<ast::DocComment<M>>) -> ConversionResult<String> {
175 Ok(inp
176 .into_iter()
177 .map(|i| i.1.strip_prefix("///").unwrap_or(&i.1).trim().to_string())
178 .collect::<Vec<_>>()
179 .join("\n"))
180}
181
182fn convert_expression<M: AstInfo>(inp: ast::Expression<M>) -> ConversionResult<Expression> {
183 Ok(match inp {
184 ast::Expression::Star(_, exp) => Expression::Repeat {
185 e: Box::new(convert_expression(*exp)?),
186 min: 0,
187 max: None,
188 },
189 ast::Expression::Plus(_, exp) => Expression::Repeat {
190 e: Box::new(convert_expression(*exp)?),
191 min: 1,
192 max: None,
193 },
194 ast::Expression::Maybe(_, exp) => Expression::Repeat {
195 e: Box::new(convert_expression(*exp)?),
196 min: 0,
197 max: Some(1),
198 },
199 ast::Expression::RepeatExact(_, exp, num) => {
200 let converted_num = convert_number(num)?;
201 Expression::Repeat {
202 e: Box::new(convert_expression(*exp)?),
203 min: converted_num,
204 max: Some(converted_num),
205 }
206 }
207 ast::Expression::RepeatRange(_, exp, min, max) => Expression::Repeat {
208 e: Box::new(convert_expression(*exp)?),
209 min: convert_number(min)?,
210 max: Some(convert_number(max)?),
211 },
212 ast::Expression::RepeatLower(_, exp, num) => Expression::Repeat {
213 e: Box::new(convert_expression(*exp)?),
214 min: convert_number(num)?,
215 max: None,
216 },
217 ast::Expression::Delimited(_, exp, delim, bound, trailing) => {
218 let (min, max) = match bound {
219 DelimitedBound::NumNum(_, min, max) => {
220 (convert_number(min)?, Some(convert_number(max)?))
221 }
222 DelimitedBound::NumInf(_, min) => (convert_number(min)?, None),
223 DelimitedBound::Num(_, min) => (convert_number(min)?, None),
224 DelimitedBound::Star(_) => (0, None),
225 DelimitedBound::Plus(_) => (1, None),
226 };
227 Expression::Delimited {
228 e: Box::new(convert_expression(*exp)?),
229 delim: Box::new(convert_expression(*delim)?),
230 min,
231 max,
232 trailing,
233 }
234 }
235 ast::Expression::Literal(_, l) => Expression::Literal(l.to_string()),
236 ast::Expression::Sort(_, s) => Expression::Sort(convert_identifier(&s)),
237 ast::Expression::Class(_, cc) => Expression::CharacterClass(convert_character_class(cc)?),
238 ast::Expression::Paren(_, exp) => {
239 convert_expressions(exp.into_iter().map(|i| *i).collect())?
240 }
241 ast::Expression::Labelled(_, _, _) => todo!(),
242 })
243}
244
245fn convert_expressions<M: AstInfo>(
246 mut inp: Vec<ast::Expression<M>>,
247) -> ConversionResult<Expression> {
248 if inp.len() == 1 {
250 convert_expression(inp.pop().unwrap())
251 } else {
252 Ok(Expression::Sequence(
253 inp.into_iter()
254 .map(|i| convert_expression(i))
255 .collect::<Result<_, _>>()?,
256 ))
257 }
258}
259
260impl<M> ToString for ast::String<M> {
261 fn to_string(&self) -> String {
262 let chars = match self {
263 ast::String::Single(_, s) => s,
264 ast::String::Double(_, s) => s,
265 };
266 chars.iter().map(|i| convert_string_char(i)).collect()
267 }
268}
269
270fn convert_annotations<M: AstInfo>(
271 inp: &ast::AnnotationList<M>,
272) -> ConversionResult<Vec<Annotation>> {
273 let ast::AnnotationList(_, annotations) = inp;
274 annotations
275 .iter()
276 .map(|an: &ast::Annotation<M>| {
277 Ok(match an {
278 ast::Annotation::Injection(_) => Annotation::Injection,
279 ast::Annotation::NoPrettyPrint(_) => Annotation::NoPrettyPrint,
280 ast::Annotation::SingleString(_) => Annotation::SingleString,
281 ast::Annotation::NoLayout(_) => Annotation::NoLayout,
282 ast::Annotation::Hidden(_) => Annotation::Hidden,
283 ast::Annotation::Error(_, msg) => Annotation::Error(msg.to_string()),
284 ast::Annotation::PartOf(_, name) => Annotation::PartOf(convert_identifier(name)),
285 })
286 })
287 .collect::<Result<_, _>>()
288}
289
290fn convert_constructor<M: AstInfo>(inp: ast::Constructor<M>) -> ConversionResult<Constructor> {
291 Ok(match inp {
292 ast::Constructor::Constructor(_, name, expressions, annotations) => Constructor {
293 documentation: None,
294 name: convert_identifier(&name),
295 expression: convert_expressions(expressions)?,
296 annotations: if let Some(a) = annotations {
297 convert_annotations(&a)?
298 } else {
299 Vec::new()
300 },
301 dont_put_in_ast: false,
302 },
303 ast::Constructor::ConstructorDocumented(_, comments, constructor) => {
304 convert_constructor(*constructor).and_then(|mut i| {
305 i.documentation = Some(convert_comments(comments)?);
306 Ok(i)
307 })?
308 }
309 ast::Constructor::ConstructorBare(_, name, annotations) => Constructor {
310 documentation: None,
311 name: convert_identifier(&name),
312 expression: Expression::Sort(convert_identifier(&name)),
313 annotations: annotations.map_or(Ok(vec![]), |i| convert_annotations(&i))?,
314 dont_put_in_ast: false,
315 },
316 })
317}