Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macro declaration syntax. #7075

Open
wants to merge 1 commit into
base: high-level-inline-macros
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
11 changes: 10 additions & 1 deletion crates/cairo-lang-formatter/src/node_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ impl SyntaxNodeFormat for SyntaxNode {
| SyntaxKind::TokenColonColon
| SyntaxKind::TokenLParen
| SyntaxKind::TokenLBrack
| SyntaxKind::TokenImplicits => true,
| SyntaxKind::TokenImplicits
| SyntaxKind::TokenDollar => true,
SyntaxKind::TerminalDotDot
if matches!(parent_kind(db, self), Some(SyntaxKind::ExprBinary)) =>
{
Expand Down Expand Up @@ -432,6 +433,14 @@ impl SyntaxNodeFormat for SyntaxNode {
false,
))
}
Some(SyntaxKind::MacroRulesList) => {
BreakLinePointsPositions::new_symmetric(BreakLinePointProperties::new(
21,
BreakLinePointIndentation::IndentedWithTail,
false,
true,
))
}
_ => match self.kind(db) {
SyntaxKind::ExprList | SyntaxKind::ImplicitsList | SyntaxKind::PatternList => {
BreakLinePointsPositions::new_symmetric(BreakLinePointProperties::new(
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
70 changes: 69 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,15 @@ impl<'a> Parser<'a> {
ExprBlock::new_green(self.db, lbrace, statements, rbrace)
}

/// Parses an expr block, while allowing placeholder expressions. Restores the previous
/// placeholder expression setting after parsing.
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 +2031,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
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
Loading