Skip to content

Commit 71d91f7

Browse files
Add macro declaration syntax.
1 parent d24ed19 commit 71d91f7

File tree

14 files changed

+1181
-22
lines changed

14 files changed

+1181
-22
lines changed

crates/cairo-lang-defs/src/db.rs

+1
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe<ModuleData
549549
),
550550
),
551551
)),
552+
ast::ModuleItem::MacroDeclaration(_) => todo!(),
552553
ast::ModuleItem::HeaderDoc(_) => {}
553554
ast::ModuleItem::Missing(_) => {}
554555
}

crates/cairo-lang-parser/src/lexer.rs

+6
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ impl<'a> Lexer<'a> {
213213
"let" => TokenKind::Let,
214214
"return" => TokenKind::Return,
215215
"match" => TokenKind::Match,
216+
"macro" => TokenKind::Macro,
216217
"if" => TokenKind::If,
217218
"loop" => TokenKind::Loop,
218219
"continue" => TokenKind::Continue,
@@ -276,6 +277,7 @@ impl<'a> Lexer<'a> {
276277
'%' => self.pick_kind('=', TokenKind::ModEq, TokenKind::Mod),
277278
'+' => self.pick_kind('=', TokenKind::PlusEq, TokenKind::Plus),
278279
'#' => self.take_token_of_kind(TokenKind::Hash),
280+
'$' => self.take_token_of_kind(TokenKind::Dollar),
279281
'-' => {
280282
self.take();
281283
match self.peek() {
@@ -375,6 +377,7 @@ enum TokenKind {
375377
Let,
376378
Return,
377379
Match,
380+
Macro,
378381
If,
379382
While,
380383
For,
@@ -420,6 +423,7 @@ enum TokenKind {
420423
Colon,
421424
ColonColon,
422425
Comma,
426+
Dollar,
423427
Dot,
424428
DotDot,
425429
Eq,
@@ -474,6 +478,7 @@ fn token_kind_to_terminal_syntax_kind(kind: TokenKind) -> SyntaxKind {
474478
TokenKind::Implicits => SyntaxKind::TerminalImplicits,
475479
TokenKind::NoPanic => SyntaxKind::TerminalNoPanic,
476480
TokenKind::Pub => SyntaxKind::TerminalPub,
481+
TokenKind::Macro => SyntaxKind::TerminalMacro,
477482
TokenKind::And => SyntaxKind::TerminalAnd,
478483
TokenKind::AndAnd => SyntaxKind::TerminalAndAnd,
479484
TokenKind::At => SyntaxKind::TerminalAt,
@@ -501,6 +506,7 @@ fn token_kind_to_terminal_syntax_kind(kind: TokenKind) -> SyntaxKind {
501506
TokenKind::Colon => SyntaxKind::TerminalColon,
502507
TokenKind::ColonColon => SyntaxKind::TerminalColonColon,
503508
TokenKind::Comma => SyntaxKind::TerminalComma,
509+
TokenKind::Dollar => SyntaxKind::TerminalDollar,
504510
TokenKind::Dot => SyntaxKind::TerminalDot,
505511
TokenKind::DotDot => SyntaxKind::TerminalDotDot,
506512
TokenKind::Eq => SyntaxKind::TerminalEq,

crates/cairo-lang-parser/src/parser.rs

+69-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct Parser<'a> {
4141
diagnostics: &'a mut DiagnosticsBuilder<ParserDiagnostic>,
4242
/// An accumulating vector of pending skipped tokens diagnostics.
4343
pending_skipped_token_diagnostics: Vec<PendingParserDiagnostic>,
44+
/// An indicator of whether to allow placeholders exprs.
45+
allow_placeholder_exprs: bool,
4446
}
4547

4648
/// The possible results of a try_parse_* function failing to parse.
@@ -120,6 +122,7 @@ impl<'a> Parser<'a> {
120122
last_trivia_length: Default::default(),
121123
diagnostics,
122124
pending_skipped_token_diagnostics: Vec::new(),
125+
allow_placeholder_exprs: false,
123126
}
124127
}
125128

@@ -242,6 +245,9 @@ impl<'a> Parser<'a> {
242245
SyntaxKind::TerminalUse => Ok(self.expect_item_use(attributes, visibility).into()),
243246
SyntaxKind::TerminalTrait => Ok(self.expect_item_trait(attributes, visibility).into()),
244247
SyntaxKind::TerminalImpl => Ok(self.expect_module_item_impl(attributes, visibility)),
248+
SyntaxKind::TerminalMacro => {
249+
Ok(self.expect_item_macro_declaration(attributes, visibility).into())
250+
}
245251
SyntaxKind::TerminalIdentifier => {
246252
// We take the identifier to check if the next token is a `!`. If it is, we assume
247253
// that a macro is following and handle it similarly to any other module item. If
@@ -566,6 +572,48 @@ impl<'a> Parser<'a> {
566572
ItemUse::new_green(self.db, attributes, visibility, use_kw, use_path, semicolon)
567573
}
568574

575+
/// Assumes the current token is Macro.
576+
/// Expected pattern: `macro<Identifier>{<MacroRulesList>}`
577+
fn expect_item_macro_declaration(
578+
&mut self,
579+
attributes: AttributeListGreen,
580+
visibility: VisibilityGreen,
581+
) -> ItemMacroDeclarationGreen {
582+
let macro_kw = self.take::<TerminalMacro>();
583+
let name = self.parse_identifier();
584+
let lbrace = self.parse_token::<TerminalLBrace>();
585+
let macro_rules = MacroRulesList::new_green(
586+
self.db,
587+
self.parse_list(Self::try_parse_macro_rule, is_of_kind!(rbrace), "macro rule"),
588+
);
589+
let rbrace = self.parse_token::<TerminalRBrace>();
590+
ItemMacroDeclaration::new_green(
591+
self.db,
592+
attributes,
593+
visibility,
594+
macro_kw,
595+
name,
596+
lbrace,
597+
macro_rules,
598+
rbrace,
599+
)
600+
}
601+
602+
/// Returns a GreenId of a node with a MacroRule kind or TryParseFailure if a macro rule can't
603+
/// be parsed.
604+
fn try_parse_macro_rule(&mut self) -> TryParseResult<MacroRuleGreen> {
605+
match self.peek().kind {
606+
SyntaxKind::TerminalLParen => {
607+
let lhs = self.parse_token_tree_node();
608+
let arrow = self.parse_token::<TerminalMatchArrow>();
609+
let rhs = self.parse_block_with_placeholders();
610+
let semicolon = self.parse_token::<TerminalSemicolon>();
611+
Ok(MacroRule::new_green(self.db, lhs, arrow, rhs, semicolon))
612+
}
613+
_ => Err(TryParseFailure::SkipToken),
614+
}
615+
}
616+
569617
/// Returns a GreenId of a node with a UsePath kind or TryParseFailure if can't parse a UsePath.
570618
fn try_parse_use_path(&mut self) -> TryParseResult<UsePathGreen> {
571619
if !matches!(self.peek().kind, SyntaxKind::TerminalLBrace | SyntaxKind::TerminalIdentifier)
@@ -1244,7 +1292,9 @@ impl<'a> Parser<'a> {
12441292
SyntaxKind::TerminalOrOr if lbrace_allowed == LbraceAllowed::Allow => {
12451293
Ok(self.expect_closure_expr_nullary().into())
12461294
}
1247-
1295+
SyntaxKind::TerminalDollar if self.allow_placeholder_exprs => {
1296+
Ok(self.expect_placeholder_expr().into())
1297+
}
12481298
_ => {
12491299
// TODO(yuval): report to diagnostics.
12501300
Err(TryParseFailure::SkipToken)
@@ -1434,6 +1484,7 @@ impl<'a> Parser<'a> {
14341484
SyntaxKind::TerminalComma => self.take::<TerminalComma>().into(),
14351485
SyntaxKind::TerminalDiv => self.take::<TerminalDiv>().into(),
14361486
SyntaxKind::TerminalDivEq => self.take::<TerminalDivEq>().into(),
1487+
SyntaxKind::TerminalDollar => self.take::<TerminalDollar>().into(),
14371488
SyntaxKind::TerminalDot => self.take::<TerminalDot>().into(),
14381489
SyntaxKind::TerminalDotDot => self.take::<TerminalDotDot>().into(),
14391490
SyntaxKind::TerminalEndOfFile => self.take::<TerminalEndOfFile>().into(),
@@ -1805,6 +1856,15 @@ impl<'a> Parser<'a> {
18051856
ExprBlock::new_green(self.db, lbrace, statements, rbrace)
18061857
}
18071858

1859+
/// Parses an expr block, while allowing placeholder expressions. Restores the previous
1860+
/// placeholder expression setting after parsing.
1861+
fn parse_block_with_placeholders(&mut self) -> ExprBlockGreen {
1862+
let prev_allow_placeholder_exprs = self.allow_placeholder_exprs;
1863+
self.allow_placeholder_exprs = true;
1864+
let block = self.parse_block();
1865+
self.allow_placeholder_exprs = prev_allow_placeholder_exprs;
1866+
block
1867+
}
18081868
/// Assumes the current token is `Match`.
18091869
/// Expected pattern: `match <expr> \{<MatchArm>*\}`
18101870
fn expect_match_expr(&mut self) -> ExprMatchGreen {
@@ -1971,6 +2031,14 @@ impl<'a> Parser<'a> {
19712031
)
19722032
}
19732033

2034+
/// Assumes the current token is Dollar.
2035+
/// Expected pattern: `$<identifier>`.
2036+
fn expect_placeholder_expr(&mut self) -> ExprPlaceholderGreen {
2037+
let dollar = self.take::<TerminalDollar>();
2038+
let name = self.parse_identifier();
2039+
ExprPlaceholder::new_green(self.db, dollar, name)
2040+
}
2041+
19742042
/// Returns a GreenId of a node with a MatchArm kind or TryParseFailure if a match arm can't be
19752043
/// parsed.
19762044
pub fn try_parse_match_arm(&mut self) -> TryParseResult<MatchArmGreen> {

crates/cairo-lang-parser/src/parser_test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ cairo_lang_test_utils::test_file_test!(
193193
range: "range",
194194
use_: "use",
195195
type_alias: "type_alias",
196+
macro_declaration: "macro_declaration",
196197
},
197198
test_partial_parser_tree
198199
);

crates/cairo-lang-parser/src/parser_test_data/partial_trees/item_inline_macro

+8-8
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ identifier
317317
test_partial_parser_tree(expect_diagnostics: true)
318318

319319
//! > cairo_code
320-
macro!
320+
a_macro!
321321
fn foo() {}
322322

323323
//! > top_level_kind
@@ -328,20 +328,20 @@ FunctionWithBody
328328

329329
//! > expected_diagnostics
330330
error: Missing tokens. Expected an argument list wrapped in either parentheses, brackets, or braces.
331-
--> dummy_file.cairo:1:7
332-
macro!
333-
^
331+
--> dummy_file.cairo:1:9
332+
a_macro!
333+
^
334334

335335
error: Missing token TerminalSemicolon.
336-
--> dummy_file.cairo:1:7
337-
macro!
338-
^
336+
--> dummy_file.cairo:1:9
337+
a_macro!
338+
^
339339

340340
//! > expected_tree
341341
└── Top level kind: ModuleItemList
342342
├── child #0 (kind: ItemInlineMacro)
343343
│ ├── attributes (kind: AttributeList) []
344-
│ ├── name (kind: TokenIdentifier): 'macro'
344+
│ ├── name (kind: TokenIdentifier): 'a_macro'
345345
│ ├── bang (kind: TokenNot): '!'
346346
│ ├── arguments (kind: TokenTreeNode)
347347
│ │ └── subtree (kind: WrappedTokenTreeMissing) []

0 commit comments

Comments
 (0)