lwb_parser/parser/syntax_file/
convert_syntax_file_ast.rs

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                // .filter(|i| match i {
141                //     ast::Constructor::ConstructorDocumented(_, _, c) => true,
142                //     ast::Constructor::Constructor(_, n, _, _) => n.1 != "double-eq",
143                // })
144                .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    // 0 not possible due to grammar constraints
249    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}