cairo_lang_parser/
macro_helpers.rs

1use cairo_lang_diagnostics::DiagnosticsBuilder;
2use cairo_lang_filesystem::ids::FileId;
3use cairo_lang_filesystem::span::TextSpan;
4use cairo_lang_syntax::node::ast::{
5    self, AttributeListGreen, ExprInlineMacro, ExprPathGreen, ItemInlineMacro,
6    LegacyExprInlineMacro, LegacyItemInlineMacro, TerminalNotGreen, TerminalSemicolonGreen,
7    TokenTree, TokenTreeNode, WrappedArgListGreen,
8};
9use cairo_lang_syntax::node::db::SyntaxGroup;
10use cairo_lang_syntax::node::kind::SyntaxKind;
11use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
12
13use crate::ParserDiagnostic;
14use crate::diagnostic::ParserDiagnosticKind;
15use crate::parser::{Parser, SkippedError};
16use crate::recovery::is_of_kind;
17
18/// Takes a token tree syntax node, which is assumed to be parsable as a wrapped argument list, try
19/// to parse it as such and return the result.
20pub fn token_tree_as_wrapped_arg_list(
21    token_tree: TokenTreeNode,
22    db: &dyn SyntaxGroup,
23) -> Option<WrappedArgListGreen> {
24    let mut diagnostics: DiagnosticsBuilder<ParserDiagnostic> = DiagnosticsBuilder::default();
25    let node_text = token_tree.as_syntax_node().get_text(db);
26    let file_id = token_tree.stable_ptr(db).0.file_id(db);
27    let mut parser = Parser::new(db, file_id, &node_text, &mut diagnostics);
28    let wrapped_arg_list_green = parser.parse_wrapped_arg_list();
29    if let Err(SkippedError(span)) = parser.skip_until(is_of_kind!()) {
30        parser.add_diagnostic(
31            ParserDiagnosticKind::SkippedElement { element_name: "end arg list".into() },
32            span,
33        );
34    };
35    let diagnostics = diagnostics.build();
36    if !diagnostics.get_all().is_empty() {
37        return None;
38    }
39    Some(wrapped_arg_list_green)
40}
41
42/// Takes a token tree syntax node, which is assumed to be parsable as an expression (it assumes
43/// that the prefix is an expr, not the whole iterator), tries to parse it as such, and returns the
44/// result. The token tree iterator is consumed entirely. The resulting expression's offset
45/// corresponds to the offset of the first token in the provided token tree.
46pub fn as_expr_macro_token_tree(
47    mut token_tree: impl DoubleEndedIterator<Item = TokenTree>,
48    file_id: FileId,
49    db: &dyn SyntaxGroup,
50) -> Option<ast::Expr> {
51    let mut diagnostics: DiagnosticsBuilder<ParserDiagnostic> = DiagnosticsBuilder::default();
52    let first_token = token_tree.next()?.as_syntax_node();
53    let last_token =
54        token_tree.next_back().map(|last| last.as_syntax_node()).unwrap_or(first_token);
55    let file_content = db.file_content(file_id).expect("Failed to read file content");
56
57    let start = first_token.offset(db);
58    let end = last_token.span(db).end;
59    let span = TextSpan { start, end };
60
61    let mut parser = Parser::new(db, file_id, span.take(&file_content), &mut diagnostics);
62    let expr_green = parser.parse_expr();
63    let expr = ast::Expr::from_syntax_node(
64        db,
65        SyntaxNode::new_root_with_offset(db, file_id, expr_green.0, Some(first_token.offset(db))),
66    );
67    Some(expr)
68}
69
70/// Trait for converting inline macros with token tree syntax as the argument to legacy inline which
71/// must have a wrapped argument list syntax node.
72pub trait AsLegacyInlineMacro {
73    /// The corresponding legacy inline macro type.
74    type LegacyType;
75    /// Converts the inline macro to the legacy inline macro.
76    fn as_legacy_inline_macro(&self, db: &dyn SyntaxGroup) -> Option<Self::LegacyType>;
77}
78
79impl AsLegacyInlineMacro for ExprInlineMacro {
80    type LegacyType = LegacyExprInlineMacro;
81
82    fn as_legacy_inline_macro(&self, db: &dyn SyntaxGroup) -> Option<Self::LegacyType> {
83        let green_node = self.as_syntax_node().green_node(db);
84        let [macro_name, bang, _arguments] = green_node.children() else {
85            return None;
86        };
87        let macro_name = ExprPathGreen(*macro_name);
88        let bang = TerminalNotGreen(*bang);
89        let wrapped_arg_list = token_tree_as_wrapped_arg_list(self.arguments(db), db)?;
90        let legacy_green = LegacyExprInlineMacro::new_green(db, macro_name, bang, wrapped_arg_list);
91        let file_id = self.stable_ptr(db).0.file_id(db);
92        let offset = self.stable_ptr(db).0.lookup(db).offset(db);
93        Some(LegacyExprInlineMacro::from_syntax_node(
94            db,
95            SyntaxNode::new_root_with_offset(db, file_id, legacy_green.0, Some(offset)),
96        ))
97    }
98}
99
100impl AsLegacyInlineMacro for ItemInlineMacro {
101    type LegacyType = LegacyItemInlineMacro;
102
103    fn as_legacy_inline_macro(&self, db: &dyn SyntaxGroup) -> Option<Self::LegacyType> {
104        let green_node = self.as_syntax_node().green_node(db);
105        let [attributes, macro_name, bang, _arguments, semicolon] = green_node.children() else {
106            return None;
107        };
108        let attributes = AttributeListGreen(*attributes);
109        let macro_path = ExprPathGreen(*macro_name);
110        let bang = TerminalNotGreen(*bang);
111        let wrapped_arg_list = token_tree_as_wrapped_arg_list(self.arguments(db), db)?;
112        let semicolon = TerminalSemicolonGreen(*semicolon);
113        let legacy_green = LegacyItemInlineMacro::new_green(
114            db,
115            attributes,
116            macro_path,
117            bang,
118            wrapped_arg_list,
119            semicolon,
120        );
121        let file_id = self.stable_ptr(db).0.file_id(db);
122        let offset = self.stable_ptr(db).0.lookup(db).offset(db);
123        Some(LegacyItemInlineMacro::from_syntax_node(
124            db,
125            SyntaxNode::new_root_with_offset(db, file_id, legacy_green.0, Some(offset)),
126        ))
127    }
128}