Skip to main content

cairo_lang_syntax_codegen/
spec.rs

1// Representation of the AST specifications.
2#[derive(Clone)]
3pub struct Node {
4    pub name: String,
5    pub kind: NodeKind,
6}
7
8/// The variants of an enum node.
9#[derive(Clone)]
10pub enum Variants {
11    /// Explicit list of variants.
12    List(Vec<Variant>),
13    /// The variants are all the tokens in the language.
14    AllTokens,
15}
16#[derive(Clone)]
17pub enum NodeKind {
18    Enum { variants: Variants, missing_variant: bool },
19    Struct { members: Vec<Member> },
20    Terminal { is_keyword: bool, members: Vec<Member> },
21    List { element_type: String },
22    SeparatedList { element_type: String, separator_type: String },
23    Token { is_keyword: bool },
24    Missing,
25}
26#[derive(Clone)]
27pub struct Member {
28    pub name: String,
29    pub kind: String,
30    /// Whether this member serves as a key in the stable pointer of this syntax node.
31    /// See `syntax::node::stable_ptr`.
32    pub key: bool,
33}
34#[derive(Clone)]
35pub struct Variant {
36    pub name: String,
37    pub kind: String,
38}
39
40// Helpers to build AST specifications.
41
42/// Builds a spec for a struct node.
43pub struct StructBuilder {
44    name: String,
45    members: Vec<Member>,
46    is_terminal: bool,
47    is_keyword: bool,
48}
49impl StructBuilder {
50    pub fn new(name: &str) -> Self {
51        Self { name: name.into(), members: Vec::new(), is_terminal: false, is_keyword: false }
52    }
53    pub fn new_terminal(name: &str, is_keyword: bool) -> Self {
54        Self { name: name.into(), members: Vec::new(), is_terminal: true, is_keyword }
55    }
56    pub fn node(mut self, field: &str, kind: &str) -> StructBuilder {
57        self.members.push(Member { name: field.into(), kind: kind.into(), key: false });
58        self
59    }
60    pub fn key_node(mut self, field: &str, kind: &str) -> StructBuilder {
61        self.members.push(Member { name: field.into(), kind: kind.into(), key: true });
62        self
63    }
64    pub fn build(self) -> Node {
65        Node {
66            name: self.name,
67            kind: if self.is_terminal {
68                NodeKind::Terminal { is_keyword: self.is_keyword, members: self.members }
69            } else {
70                NodeKind::Struct { members: self.members }
71            },
72        }
73    }
74}
75/// Builds a spec for an enum node.
76pub struct EnumBuilder {
77    name: String,
78    variants: Vec<Variant>,
79    include_missing_variant: bool,
80}
81impl EnumBuilder {
82    pub fn new(name: &str) -> Self {
83        Self { name: name.into(), variants: Vec::new(), include_missing_variant: false }
84    }
85    /// Adds a `Missing` variant to the enum, whose kind is an empty `{enum_name}Missing` node,
86    /// created automatically by [`NodesAggregator::add_enum`].
87    pub fn include_missing(mut self) -> EnumBuilder {
88        self.include_missing_variant = true;
89        self
90    }
91    pub fn node(self, name: &str) -> EnumBuilder {
92        let kind_name = self.name.clone() + name;
93        self.node_with_explicit_kind(name, &kind_name)
94    }
95    pub fn node_with_explicit_kind(mut self, name: &str, kind: &str) -> EnumBuilder {
96        self.variants.push(Variant { name: name.to_string(), kind: kind.to_string() });
97        self
98    }
99    pub fn build(mut self) -> Node {
100        if self.include_missing_variant {
101            self.variants
102                .push(Variant { name: "Missing".into(), kind: self.name.clone() + "Missing" });
103        }
104        Node {
105            name: self.name,
106            kind: NodeKind::Enum {
107                variants: Variants::List(self.variants),
108                missing_variant: self.include_missing_variant,
109            },
110        }
111    }
112}
113
114/// A tool to aggregate/gather nodes in various forms and eventually emit them as a vector.
115#[derive(Default)]
116pub struct NodesAggregator {
117    nodes: Vec<Node>,
118}
119impl NodesAggregator {
120    /// Gets all the aggregated nodes.
121    pub fn get(self) -> Vec<Node> {
122        self.nodes
123    }
124
125    /// Adds a struct node.
126    pub fn add_struct(mut self, builder: StructBuilder) -> Self {
127        self.nodes.push(builder.build());
128        self
129    }
130
131    /// Adds an enum node, as well as the empty node for its `Missing` variant if it has one.
132    pub fn add_enum(mut self, builder: EnumBuilder) -> Self {
133        if builder.include_missing_variant {
134            self.nodes
135                .push(Node { name: builder.name.clone() + "Missing", kind: NodeKind::Missing });
136        }
137        self.nodes.push(builder.build());
138        self
139    }
140
141    /// Adds a node for a list of syntax elements.
142    pub fn add_list(mut self, name: &str, element_type: &str) -> Self {
143        self.nodes.push(Node {
144            name: name.into(),
145            kind: NodeKind::List { element_type: element_type.into() },
146        });
147        self
148    }
149
150    /// Adds a node for a list of syntax elements separated by a terminal.
151    pub fn add_separated_list(
152        mut self,
153        name: &str,
154        element_type: &str,
155        separator_type: &str,
156    ) -> Self {
157        self.nodes.push(Node {
158            name: name.into(),
159            kind: NodeKind::SeparatedList {
160                element_type: element_type.into(),
161                separator_type: separator_type.into(),
162            },
163        });
164        self
165    }
166
167    /// Adds a non-keyword node for a token node (similar to an empty struct).
168    pub fn add_token(mut self, pure_name: &str) -> Self {
169        self.nodes.push(Node {
170            name: format!("Token{pure_name}"),
171            kind: NodeKind::Token { is_keyword: false },
172        });
173        self
174    }
175
176    /// Adds a keyword node for a token node (similar to an empty struct).
177    pub fn add_keyword_token(mut self, pure_name: &str) -> Self {
178        self.nodes.push(Node {
179            name: format!("Token{pure_name}"),
180            kind: NodeKind::Token { is_keyword: true },
181        });
182        self
183    }
184
185    /// Adds a node for a token node (similar to an empty struct).
186    pub fn add_terminal(self, pure_name: &str, is_keyword: bool) -> Self {
187        self.add_struct(
188            StructBuilder::new_terminal(format!("Terminal{pure_name}").as_str(), is_keyword)
189                .node("leading_trivia", "Trivia")
190                .node("token", format!("Token{pure_name}").as_str())
191                .node("trailing_trivia", "Trivia"),
192        )
193    }
194
195    /// Adds a keyword token node and a keyword terminal node of the relevant names. e.g. for
196    /// pure_name="Identifier" it creates TokenIdentifier and TerminalIdentifier.
197    pub fn add_keyword_token_and_terminal(self, pure_name: &str) -> Self {
198        self.add_keyword_token(pure_name).add_terminal(pure_name, true)
199    }
200
201    /// Adds a non-keyword token node and a non-keyword terminal node of the relevant names. e.g.
202    /// for pure_name="Identifier" it creates TokenIdentifier and TerminalIdentifier.
203    pub fn add_token_and_terminal(self, pure_name: &str) -> Self {
204        self.add_token(pure_name).add_terminal(pure_name, false)
205    }
206
207    /// Adds an enum node for an option with 2 variants: empty and non-empty. Creates the empty
208    /// struct to be used for the empty variant. The Type for the non-empty variant is `name`
209    /// and it should exist independently of this call.
210    ///
211    /// For example, for name=TypeClause, creates an enum OptionTypeClause with variants
212    /// Empty(OptionTypeClauseEmpty) and TypeClause(TypeClause), where OptionTypeClauseEmpty is
213    /// created here and TypeClause should exist independently.
214    pub fn add_option(self, name: &str) -> Self {
215        self.add_enum(
216            EnumBuilder::new(format!("Option{name}").as_str())
217                .node("Empty")
218                .node_with_explicit_kind(name, name),
219        )
220        .add_struct(StructBuilder::new(format!("Option{name}Empty").as_str()))
221    }
222
223    /// Adds an enum containing all the tokens as variants.
224    pub fn add_all_tokens_enum(mut self, name: &str) -> Self {
225        self.nodes.push(Node {
226            name: name.into(),
227            kind: NodeKind::Enum { variants: Variants::AllTokens, missing_variant: false },
228        });
229        self
230    }
231}