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