Skip to content

Commit

Permalink
Add macro declaration syntax.
Browse files Browse the repository at this point in the history
  • Loading branch information
gilbens-starkware committed Jan 14, 2025
1 parent d24ed19 commit 3514fb0
Show file tree
Hide file tree
Showing 14 changed files with 1,184 additions and 22 deletions.
1 change: 1 addition & 0 deletions crates/cairo-lang-defs/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe<ModuleData
),
),
)),
ast::ModuleItem::MacroDeclaration(_) => todo!(),
ast::ModuleItem::HeaderDoc(_) => {}
ast::ModuleItem::Missing(_) => {}
}
Expand Down
6 changes: 6 additions & 0 deletions crates/cairo-lang-parser/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ impl<'a> Lexer<'a> {
"let" => TokenKind::Let,
"return" => TokenKind::Return,
"match" => TokenKind::Match,
"macro" => TokenKind::Macro,
"if" => TokenKind::If,
"loop" => TokenKind::Loop,
"continue" => TokenKind::Continue,
Expand Down Expand Up @@ -276,6 +277,7 @@ impl<'a> Lexer<'a> {
'%' => self.pick_kind('=', TokenKind::ModEq, TokenKind::Mod),
'+' => self.pick_kind('=', TokenKind::PlusEq, TokenKind::Plus),
'#' => self.take_token_of_kind(TokenKind::Hash),
'$' => self.take_token_of_kind(TokenKind::Dollar),
'-' => {
self.take();
match self.peek() {
Expand Down Expand Up @@ -375,6 +377,7 @@ enum TokenKind {
Let,
Return,
Match,
Macro,
If,
While,
For,
Expand Down Expand Up @@ -420,6 +423,7 @@ enum TokenKind {
Colon,
ColonColon,
Comma,
Dollar,
Dot,
DotDot,
Eq,
Expand Down Expand Up @@ -474,6 +478,7 @@ fn token_kind_to_terminal_syntax_kind(kind: TokenKind) -> SyntaxKind {
TokenKind::Implicits => SyntaxKind::TerminalImplicits,
TokenKind::NoPanic => SyntaxKind::TerminalNoPanic,
TokenKind::Pub => SyntaxKind::TerminalPub,
TokenKind::Macro => SyntaxKind::TerminalMacro,
TokenKind::And => SyntaxKind::TerminalAnd,
TokenKind::AndAnd => SyntaxKind::TerminalAndAnd,
TokenKind::At => SyntaxKind::TerminalAt,
Expand Down Expand Up @@ -501,6 +506,7 @@ fn token_kind_to_terminal_syntax_kind(kind: TokenKind) -> SyntaxKind {
TokenKind::Colon => SyntaxKind::TerminalColon,
TokenKind::ColonColon => SyntaxKind::TerminalColonColon,
TokenKind::Comma => SyntaxKind::TerminalComma,
TokenKind::Dollar => SyntaxKind::TerminalDollar,
TokenKind::Dot => SyntaxKind::TerminalDot,
TokenKind::DotDot => SyntaxKind::TerminalDotDot,
TokenKind::Eq => SyntaxKind::TerminalEq,
Expand Down
73 changes: 72 additions & 1 deletion crates/cairo-lang-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub struct Parser<'a> {
diagnostics: &'a mut DiagnosticsBuilder<ParserDiagnostic>,
/// An accumulating vector of pending skipped tokens diagnostics.
pending_skipped_token_diagnostics: Vec<PendingParserDiagnostic>,
/// An indicator of whether to allow placeholders exprs.
allow_placeholder_exprs: bool,
}

/// The possible results of a try_parse_* function failing to parse.
Expand Down Expand Up @@ -120,6 +122,7 @@ impl<'a> Parser<'a> {
last_trivia_length: Default::default(),
diagnostics,
pending_skipped_token_diagnostics: Vec::new(),
allow_placeholder_exprs: false,
}
}

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

/// Assumes the current token is Macro.
/// Expected pattern: `macro<Identifier>{<MacroRulesList>}`
fn expect_item_macro_declaration(
&mut self,
attributes: AttributeListGreen,
visibility: VisibilityGreen,
) -> ItemMacroDeclarationGreen {
let macro_kw = self.take::<TerminalMacro>();
let name = self.parse_identifier();
let lbrace = self.parse_token::<TerminalLBrace>();
let macro_rules = MacroRulesList::new_green(
self.db,
self.parse_list(Self::try_parse_macro_rule, is_of_kind!(rbrace), "macro rule"),
);
let rbrace = self.parse_token::<TerminalRBrace>();
ItemMacroDeclaration::new_green(
self.db,
attributes,
visibility,
macro_kw,
name,
lbrace,
macro_rules,
rbrace,
)
}

/// Returns a GreenId of a node with a MacroRule kind or TryParseFailure if a macro rule can't
/// be parsed.
fn try_parse_macro_rule(&mut self) -> TryParseResult<MacroRuleGreen> {
match self.peek().kind {
SyntaxKind::TerminalLParen => {
let lhs = self.parse_token_tree_node();
let arrow = self.parse_token::<TerminalMatchArrow>();
let rhs = self.parse_block_with_placeholders();
let semicolon = self.parse_token::<TerminalSemicolon>();
Ok(MacroRule::new_green(self.db, lhs, arrow, rhs, semicolon))
}
_ => Err(TryParseFailure::SkipToken),
}
}

/// Returns a GreenId of a node with a UsePath kind or TryParseFailure if can't parse a UsePath.
fn try_parse_use_path(&mut self) -> TryParseResult<UsePathGreen> {
if !matches!(self.peek().kind, SyntaxKind::TerminalLBrace | SyntaxKind::TerminalIdentifier)
Expand Down Expand Up @@ -1244,7 +1292,9 @@ impl<'a> Parser<'a> {
SyntaxKind::TerminalOrOr if lbrace_allowed == LbraceAllowed::Allow => {
Ok(self.expect_closure_expr_nullary().into())
}

SyntaxKind::TerminalDollar if self.allow_placeholder_exprs => {
Ok(self.expect_placeholder_expr().into())
}
_ => {
// TODO(yuval): report to diagnostics.
Err(TryParseFailure::SkipToken)
Expand Down Expand Up @@ -1434,6 +1484,7 @@ impl<'a> Parser<'a> {
SyntaxKind::TerminalComma => self.take::<TerminalComma>().into(),
SyntaxKind::TerminalDiv => self.take::<TerminalDiv>().into(),
SyntaxKind::TerminalDivEq => self.take::<TerminalDivEq>().into(),
SyntaxKind::TerminalDollar => self.take::<TerminalDollar>().into(),
SyntaxKind::TerminalDot => self.take::<TerminalDot>().into(),
SyntaxKind::TerminalDotDot => self.take::<TerminalDotDot>().into(),
SyntaxKind::TerminalEndOfFile => self.take::<TerminalEndOfFile>().into(),
Expand Down Expand Up @@ -1805,6 +1856,13 @@ impl<'a> Parser<'a> {
ExprBlock::new_green(self.db, lbrace, statements, rbrace)
}

fn parse_block_with_placeholders(&mut self) -> ExprBlockGreen {
let prev_allow_placeholder_exprs = self.allow_placeholder_exprs;
self.allow_placeholder_exprs = true;
let block = self.parse_block();
self.allow_placeholder_exprs = prev_allow_placeholder_exprs;
block
}
/// Assumes the current token is `Match`.
/// Expected pattern: `match <expr> \{<MatchArm>*\}`
fn expect_match_expr(&mut self) -> ExprMatchGreen {
Expand Down Expand Up @@ -1971,6 +2029,14 @@ impl<'a> Parser<'a> {
)
}

/// Assumes the current token is Dollar.
/// Expected pattern: `$<identifier>`.
fn expect_placeholder_expr(&mut self) -> ExprPlaceholderGreen {
let dollar = self.take::<TerminalDollar>();
let name = self.parse_identifier();
ExprPlaceholder::new_green(self.db, dollar, name)
}

/// Returns a GreenId of a node with a MatchArm kind or TryParseFailure if a match arm can't be
/// parsed.
pub fn try_parse_match_arm(&mut self) -> TryParseResult<MatchArmGreen> {
Expand Down Expand Up @@ -2705,7 +2771,12 @@ impl<'a> Parser<'a> {
expected_element: &str,
) -> Vec<ElementGreen> {
let mut children: Vec<ElementGreen> = Vec::new();
let mut c = 0;
loop {
if c > 10000 {
break;
}
c += 1;
let parse_result = try_parse_list_element(self);
match parse_result {
Ok(element_green) => {
Expand Down
1 change: 1 addition & 0 deletions crates/cairo-lang-parser/src/parser_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ cairo_lang_test_utils::test_file_test!(
range: "range",
use_: "use",
type_alias: "type_alias",
macro_declaration: "macro_declaration",
},
test_partial_parser_tree
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ identifier
test_partial_parser_tree(expect_diagnostics: true)

//! > cairo_code
macro!
a_macro!
fn foo() {}

//! > top_level_kind
Expand All @@ -328,20 +328,20 @@ FunctionWithBody

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

error: Missing token TerminalSemicolon.
--> dummy_file.cairo:1:7
macro!
^
--> dummy_file.cairo:1:9
a_macro!
^

//! > expected_tree
└── Top level kind: ModuleItemList
├── child #0 (kind: ItemInlineMacro)
│ ├── attributes (kind: AttributeList) []
│ ├── name (kind: TokenIdentifier): 'macro'
│ ├── name (kind: TokenIdentifier): 'a_macro'
│ ├── bang (kind: TokenNot): '!'
│ ├── arguments (kind: TokenTreeNode)
│ │ └── subtree (kind: WrappedTokenTreeMissing) []
Expand Down
Loading

0 comments on commit 3514fb0

Please sign in to comment.