cairo_lang_parser/
macro_helpers.rs1use 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
18pub 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
42pub 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
70pub trait AsLegacyInlineMacro {
73 type LegacyType;
75 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}