Skip to main content

regast_syntax/print/
sexpr.rs

1use std::convert::Infallible;
2
3use super::rep_name;
4use crate::{AstKind, Cursor, Flow, Pattern, Visitor, visit};
5
6impl Pattern {
7    #[must_use]
8    pub fn to_sexpr(&self) -> String {
9        match visit(self, SExpr::default()) {
10            Ok(output) => output,
11            Err(error) => match error {},
12        }
13    }
14}
15
16#[derive(Default)]
17struct SExpr {
18    stack: Vec<SExprFrame>,
19    root: Option<String>,
20}
21
22struct SExprFrame {
23    head: String,
24    children: Vec<String>,
25}
26
27impl Visitor for SExpr {
28    type Output = String;
29    type Err = Infallible;
30
31    fn enter(&mut self, node: Cursor<'_>) -> Result<Flow, Self::Err> {
32        self.stack.push(SExprFrame {
33            head: node_head(node),
34            children: Vec::new(),
35        });
36        Ok(Flow::Continue)
37    }
38
39    fn leave(&mut self, _node: Cursor<'_>) -> Result<(), Self::Err> {
40        let frame = self.stack.pop().expect("enter precedes leave");
41        let completed = if frame.children.is_empty() {
42            format!("({})", frame.head)
43        } else {
44            format!("({} {})", frame.head, frame.children.join(" "))
45        };
46        if let Some(parent) = self.stack.last_mut() {
47            parent.children.push(completed);
48        } else {
49            self.root = Some(completed);
50        }
51        Ok(())
52    }
53
54    fn finish(self) -> Result<Self::Output, Self::Err> {
55        Ok(self.root.expect("pattern always has a root"))
56    }
57}
58
59fn node_head(node: Cursor<'_>) -> String {
60    match node.kind() {
61        AstKind::Empty => "empty".into(),
62        AstKind::Anchor { kind } => format!("anchor {kind:?}"),
63        AstKind::Literal { c, .. } => format!("literal {c:?}"),
64        AstKind::Dot => "dot".into(),
65        AstKind::Class { .. } => format!("class {:?}", node.text()),
66        AstKind::Group { .. } => "group".into(),
67        AstKind::Alt { .. } => "alt".into(),
68        AstKind::Concat { .. } => "concat".into(),
69        AstKind::Repeat { kind, greedy, .. } => format!(
70            "repeat {} {}",
71            rep_name(*kind),
72            if *greedy { "greedy" } else { "lazy" }
73        ),
74    }
75}