Skip to content

Commit 12ea42b

Browse files
added statement expansion to parser
1 parent d4b0eea commit 12ea42b

File tree

8 files changed

+358
-16
lines changed

8 files changed

+358
-16
lines changed

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

+71-16
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,48 @@ impl<'a> Parser<'a> {
15031503
}
15041504
}
15051505

1506+
/// Parses a placeholder repetition block inside a macro.
1507+
fn parse_placeholder_repetition_block(&mut self) -> TryParseResult<StatementGreen> {
1508+
let dollar = self.take::<TerminalDollar>();
1509+
if self.peek().kind == SyntaxKind::TerminalLParen {
1510+
let lparen = self.take::<TerminalLParen>();
1511+
let elements = StatementList::new_green(
1512+
self.db,
1513+
self.parse_list(
1514+
Self::try_parse_statement,
1515+
is_of_kind!(rparen, block, rbrace, module_item_kw),
1516+
"statement",
1517+
),
1518+
);
1519+
let rparen = self.parse_token::<TerminalRParen>();
1520+
let separator = match self.peek().kind {
1521+
SyntaxKind::TerminalComma => self.take::<TerminalComma>().into(),
1522+
_ => OptionTerminalCommaEmpty::new_green(self.db).into(),
1523+
};
1524+
let operator = match self.peek().kind {
1525+
SyntaxKind::TerminalQuestionMark => self.take::<TerminalQuestionMark>().into(),
1526+
SyntaxKind::TerminalPlus => self.take::<TerminalPlus>().into(),
1527+
SyntaxKind::TerminalMul => self.take::<TerminalMul>().into(),
1528+
_ => {
1529+
println!("Unexpected token after repetition block: {:?}", self.peek());
1530+
return Err(TryParseFailure::SkipToken);
1531+
}
1532+
};
1533+
let statement_expr = StatementExpr::new_green(
1534+
self.db,
1535+
AttributeList::new_green(self.db, vec![]),
1536+
ExprPlaceholderRepetitionBlock::new_green(
1537+
self.db, dollar, lparen, elements, rparen, separator, operator,
1538+
)
1539+
.into(),
1540+
OptionTerminalSemicolonEmpty::new_green(self.db).into(),
1541+
);
1542+
Ok(statement_expr.into())
1543+
} else {
1544+
Err(TryParseFailure::DoNothing)
1545+
}
1546+
}
1547+
15061548
/// Returns a GreenId of a node with an
15071549
/// ExprPath|ExprFunctionCall|ExprStructCtorCall|ExprParenthesized|ExprTuple kind, or
15081550
/// TryParseFailure if such an expression can't be parsed.
@@ -2528,6 +2570,10 @@ impl<'a> Parser<'a> {
25282570
Err(_) => (false, AttributeList::new_green(self.db, vec![])),
25292571
};
25302572
match self.peek().kind {
2573+
SyntaxKind::TerminalDollar => match self.parse_placeholder_repetition_block() {
2574+
Ok(statement) => Ok(statement),
2575+
Err(_) => self.parse_statement_expr(attributes, has_attrs),
2576+
},
25312577
SyntaxKind::TerminalLet => {
25322578
let let_kw = self.take::<TerminalLet>();
25332579
let pattern = self.parse_pattern();
@@ -2593,22 +2639,31 @@ impl<'a> Parser<'a> {
25932639
.into(),
25942640
)
25952641
.into()),
2596-
_ => match self.try_parse_expr() {
2597-
Ok(expr) => {
2598-
let optional_semicolon = if self.peek().kind == SyntaxKind::TerminalSemicolon {
2599-
self.take::<TerminalSemicolon>().into()
2600-
} else {
2601-
OptionTerminalSemicolonEmpty::new_green(self.db).into()
2602-
};
2603-
Ok(StatementExpr::new_green(self.db, attributes, expr, optional_semicolon)
2604-
.into())
2605-
}
2606-
Err(_) if has_attrs => Ok(self.skip_taken_node_and_return_missing::<Statement>(
2607-
attributes,
2608-
ParserDiagnosticKind::AttributesWithoutStatement,
2609-
)),
2610-
Err(err) => Err(err),
2611-
},
2642+
_ => self.parse_statement_expr(attributes, has_attrs),
2643+
}
2644+
}
2645+
2646+
/// Parses an expression and wraps it in a StatementExpr.
2647+
/// Handles optional semicolon and attributes.
2648+
fn parse_statement_expr(
2649+
&mut self,
2650+
attributes: AttributeListGreen,
2651+
has_attrs: bool,
2652+
) -> TryParseResult<StatementGreen> {
2653+
match self.try_parse_expr() {
2654+
Ok(expr) => {
2655+
let optional_semicolon = if self.peek().kind == SyntaxKind::TerminalSemicolon {
2656+
self.take::<TerminalSemicolon>().into()
2657+
} else {
2658+
OptionTerminalSemicolonEmpty::new_green(self.db).into()
2659+
};
2660+
Ok(StatementExpr::new_green(self.db, attributes, expr, optional_semicolon).into())
2661+
}
2662+
Err(_) if has_attrs => Ok(self.skip_taken_node_and_return_missing::<Statement>(
2663+
attributes,
2664+
ParserDiagnosticKind::AttributesWithoutStatement,
2665+
)),
2666+
Err(err) => Err(err),
26122667
}
26132668
}
26142669

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

+132
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,135 @@ error: Missing tokens. Expected a macro rule parameter kind.
266266
│ │ └── semicolon (kind: TokenSemicolon): ';'
267267
│ └── rbrace (kind: TokenRBrace): '}'
268268
└── eof (kind: TokenEndOfFile).
269+
270+
//! > ==========================================================================
271+
272+
//! > Test macro statement expansion.
273+
274+
//! > test_runner_name
275+
test_partial_parser_tree(expect_diagnostics: false)
276+
277+
//! > cairo_code
278+
macro array_macro2 {
279+
($($x:expr), *) => {
280+
let mut arr = ArrayTrait::new();
281+
$(
282+
arr.append($x);
283+
), *
284+
arr
285+
};
286+
}
287+
288+
//! > top_level_kind
289+
290+
//! > ignored_kinds
291+
292+
//! > expected_diagnostics^
293+
294+
//! > expected_tree
295+
└── root (kind: SyntaxFile)
296+
├── items (kind: ModuleItemList)
297+
│ └── child #0 (kind: ItemMacroDeclaration)
298+
│ ├── attributes (kind: AttributeList) []
299+
│ ├── visibility (kind: VisibilityDefault) []
300+
│ ├── macro_kw (kind: TokenMacro): 'macro'
301+
│ ├── name (kind: TokenIdentifier): 'array_macro2'
302+
│ ├── lbrace (kind: TokenLBrace): '{'
303+
│ ├── rules (kind: MacroRulesList)
304+
│ │ └── child #0 (kind: MacroRule)
305+
│ │ ├── lhs (kind: ParenthesizedMacroMatcher)
306+
│ │ │ ├── lparen (kind: TokenLParen): '('
307+
│ │ │ ├── elements (kind: MacroRuleElements)
308+
│ │ │ │ └── child #0 (kind: MacroRepetition)
309+
│ │ │ │ ├── dollar (kind: TokenDollar): '$'
310+
│ │ │ │ ├── lparen (kind: TokenLParen): '('
311+
│ │ │ │ ├── elements (kind: MacroRuleElements)
312+
│ │ │ │ │ └── child #0 (kind: MacroRuleParam)
313+
│ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
314+
│ │ │ │ │ ├── name (kind: TokenIdentifier): 'x'
315+
│ │ │ │ │ ├── colon (kind: TokenColon): ':'
316+
│ │ │ │ │ └── kind (kind: ParamExpr)
317+
│ │ │ │ │ └── expr (kind: TokenIdentifier): 'expr'
318+
│ │ │ │ ├── rparen (kind: TokenRParen): ')'
319+
│ │ │ │ ├── separator (kind: TokenComma): ','
320+
│ │ │ │ └── operator (kind: TokenMul): '*'
321+
│ │ │ └── rparen (kind: TokenRParen): ')'
322+
│ │ ├── fat_arrow (kind: TokenMatchArrow): '=>'
323+
│ │ ├── rhs (kind: ExprBlock)
324+
│ │ │ ├── lbrace (kind: TokenLBrace): '{'
325+
│ │ │ ├── statements (kind: StatementList)
326+
│ │ │ │ ├── child #0 (kind: StatementLet)
327+
│ │ │ │ │ ├── attributes (kind: AttributeList) []
328+
│ │ │ │ │ ├── let_kw (kind: TokenLet): 'let'
329+
│ │ │ │ │ ├── pattern (kind: PatternIdentifier)
330+
│ │ │ │ │ │ ├── modifiers (kind: ModifierList)
331+
│ │ │ │ │ │ │ └── child #0 (kind: TokenMut): 'mut'
332+
│ │ │ │ │ │ └── name (kind: TokenIdentifier): 'arr'
333+
│ │ │ │ │ ├── type_clause (kind: OptionTypeClauseEmpty) []
334+
│ │ │ │ │ ├── eq (kind: TokenEq): '='
335+
│ │ │ │ │ ├── rhs (kind: ExprFunctionCall)
336+
│ │ │ │ │ │ ├── path (kind: ExprPath)
337+
│ │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
338+
│ │ │ │ │ │ │ └── segments (kind: ExprPathInner)
339+
│ │ │ │ │ │ │ ├── item #0 (kind: PathSegmentSimple)
340+
│ │ │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'ArrayTrait'
341+
│ │ │ │ │ │ │ ├── separator #0 (kind: TokenColonColon): '::'
342+
│ │ │ │ │ │ │ └── item #1 (kind: PathSegmentSimple)
343+
│ │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'new'
344+
│ │ │ │ │ │ └── arguments (kind: ArgListParenthesized)
345+
│ │ │ │ │ │ ├── lparen (kind: TokenLParen): '('
346+
│ │ │ │ │ │ ├── arguments (kind: ArgList) []
347+
│ │ │ │ │ │ └── rparen (kind: TokenRParen): ')'
348+
│ │ │ │ │ └── semicolon (kind: TokenSemicolon): ';'
349+
│ │ │ │ ├── child #1 (kind: StatementExpr)
350+
│ │ │ │ │ ├── attributes (kind: AttributeList) []
351+
│ │ │ │ │ ├── expr (kind: ExprPlaceholderRepetitionBlock)
352+
│ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
353+
│ │ │ │ │ │ ├── lparen (kind: TokenLParen): '('
354+
│ │ │ │ │ │ ├── elements (kind: StatementList)
355+
│ │ │ │ │ │ │ └── child #0 (kind: StatementExpr)
356+
│ │ │ │ │ │ │ ├── attributes (kind: AttributeList) []
357+
│ │ │ │ │ │ │ ├── expr (kind: ExprBinary)
358+
│ │ │ │ │ │ │ │ ├── lhs (kind: ExprPath)
359+
│ │ │ │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
360+
│ │ │ │ │ │ │ │ │ └── segments (kind: ExprPathInner)
361+
│ │ │ │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple)
362+
│ │ │ │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'arr'
363+
│ │ │ │ │ │ │ │ ├── op (kind: TokenDot): '.'
364+
│ │ │ │ │ │ │ │ └── rhs (kind: ExprFunctionCall)
365+
│ │ │ │ │ │ │ │ ├── path (kind: ExprPath)
366+
│ │ │ │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
367+
│ │ │ │ │ │ │ │ │ └── segments (kind: ExprPathInner)
368+
│ │ │ │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple)
369+
│ │ │ │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'append'
370+
│ │ │ │ │ │ │ │ └── arguments (kind: ArgListParenthesized)
371+
│ │ │ │ │ │ │ │ ├── lparen (kind: TokenLParen): '('
372+
│ │ │ │ │ │ │ │ ├── arguments (kind: ArgList)
373+
│ │ │ │ │ │ │ │ │ └── item #0 (kind: Arg)
374+
│ │ │ │ │ │ │ │ │ ├── modifiers (kind: ModifierList) []
375+
│ │ │ │ │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed)
376+
│ │ │ │ │ │ │ │ │ └── value (kind: ExprPath)
377+
│ │ │ │ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
378+
│ │ │ │ │ │ │ │ │ └── segments (kind: ExprPathInner)
379+
│ │ │ │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple)
380+
│ │ │ │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'x'
381+
│ │ │ │ │ │ │ │ └── rparen (kind: TokenRParen): ')'
382+
│ │ │ │ │ │ │ └── semicolon (kind: TokenSemicolon): ';'
383+
│ │ │ │ │ │ ├── rparen (kind: TokenRParen): ')'
384+
│ │ │ │ │ │ ├── separator (kind: TokenComma): ','
385+
│ │ │ │ │ │ └── operator (kind: TokenMul): '*'
386+
│ │ │ │ │ └── semicolon (kind: OptionTerminalSemicolonEmpty) []
387+
│ │ │ │ └── child #2 (kind: StatementExpr)
388+
│ │ │ │ ├── attributes (kind: AttributeList) []
389+
│ │ │ │ ├── expr (kind: ExprPath)
390+
│ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
391+
│ │ │ │ │ └── segments (kind: ExprPathInner)
392+
│ │ │ │ │ └── item #0 (kind: PathSegmentSimple)
393+
│ │ │ │ │ └── ident (kind: TokenIdentifier): 'arr'
394+
│ │ │ │ └── semicolon (kind: OptionTerminalSemicolonEmpty) []
395+
│ │ │ └── rbrace (kind: TokenRBrace): '}'
396+
│ │ └── semicolon (kind: TokenSemicolon): ';'
397+
│ └── rbrace (kind: TokenRBrace): '}'
398+
└── eof (kind: TokenEndOfFile).
399+
400+
//! > expected_diagnostics

crates/cairo-lang-semantic/src/expr/compute.rs

+1
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ pub fn maybe_compute_expr_semantic(
472472
ast::Expr::For(expr) => compute_expr_for_semantic(ctx, expr),
473473
ast::Expr::Closure(expr) => compute_expr_closure_semantic(ctx, expr, None),
474474
ast::Expr::Placeholder(_) => todo!(),
475+
ast::Expr::PlaceholderRepetitionBlock(_) => todo!(),
475476
}
476477
}
477478

crates/cairo-lang-syntax-codegen/src/cairo_spec.rs

+9
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub fn get_spec() -> Vec<Node> {
4242
.node("InlineMacro")
4343
.node("FixedSizeArray")
4444
.node("Placeholder")
45+
.node("PlaceholderRepetitionBlock")
4546
)
4647
.add_separated_list("ExprList", "Expr", "TerminalComma")
4748
.add_struct(StructBuilder::new("Arg")
@@ -261,6 +262,14 @@ pub fn get_spec() -> Vec<Node> {
261262
.node("dollar", "TerminalDollar")
262263
.node("path", "ExprPath")
263264
)
265+
.add_struct(StructBuilder::new("ExprPlaceholderRepetitionBlock")
266+
.node("dollar", "TerminalDollar")
267+
.node("lparen", "TerminalLParen")
268+
.node("elements", "StatementList")
269+
.node("rparen", "TerminalRParen")
270+
.node("separator", "OptionTerminalComma")
271+
.node("operator", "MacroRepetitionOperator")
272+
)
264273
// --- Struct ctor ---
265274
.add_struct(StructBuilder::new("StructArgExpr")
266275
.node("colon", "TerminalColon")

0 commit comments

Comments
 (0)