Skip to content

Commit 888bd51

Browse files
added statement expansion to parser
1 parent d4b0eea commit 888bd51

File tree

10 files changed

+475
-37
lines changed

10 files changed

+475
-37
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub enum ParserDiagnosticKind {
101101
MissingToken(SyntaxKind),
102102
MissingExpression,
103103
MissingPathSegment,
104+
MissingRepetitionOperator,
104105
MissingTypeClause,
105106
MissingTypeExpression,
106107
MissingWrappedArgList,
@@ -150,6 +151,9 @@ impl DiagnosticEntry for ParserDiagnostic {
150151
ParserDiagnosticKind::MissingTypeExpression => {
151152
"Missing tokens. Expected a type expression.".to_string()
152153
}
154+
ParserDiagnosticKind::MissingRepetitionOperator => {
155+
"Missing tokens. Expected an operator.".to_string()
156+
}
153157
ParserDiagnosticKind::MissingWrappedArgList => "Missing tokens. Expected an argument \
154158
list wrapped in either parentheses, \
155159
brackets, or braces."

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

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,38 @@ 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<StatementBlockGreen> {
1508+
let dollar = self.take::<TerminalDollar>();
1509+
if self.peek().kind != SyntaxKind::TerminalLParen {
1510+
return Err(TryParseFailure::SkipToken);
1511+
}
1512+
let lparen = self.take::<TerminalLParen>();
1513+
let elements = StatementList::new_green(
1514+
self.db,
1515+
self.parse_list(
1516+
Self::try_parse_statement,
1517+
is_of_kind!(rparen, block, rbrace, module_item_kw),
1518+
"statement",
1519+
),
1520+
);
1521+
let rparen = self.parse_token::<TerminalRParen>();
1522+
let operator = match self.peek().kind {
1523+
SyntaxKind::TerminalQuestionMark => self.take::<TerminalQuestionMark>().into(),
1524+
SyntaxKind::TerminalPlus => self.take::<TerminalPlus>().into(),
1525+
SyntaxKind::TerminalMul => self.take::<TerminalMul>().into(),
1526+
_ => self
1527+
.create_and_report_missing::<TerminalPlus>(
1528+
ParserDiagnosticKind::MissingRepetitionOperator,
1529+
)
1530+
.into(),
1531+
};
1532+
let placeholder_repetition_block = PlaceholderRepetitionBlock::new_green(
1533+
self.db, dollar, lparen, elements, rparen, operator,
1534+
);
1535+
Ok(placeholder_repetition_block.into())
1536+
}
1537+
15061538
/// Returns a GreenId of a node with an
15071539
/// ExprPath|ExprFunctionCall|ExprStructCtorCall|ExprParenthesized|ExprTuple kind, or
15081540
/// TryParseFailure if such an expression can't be parsed.
@@ -2149,12 +2181,29 @@ impl<'a> Parser<'a> {
21492181
return ExprBlock::new_green(
21502182
self.db,
21512183
self.create_and_report_missing_terminal::<TerminalLBrace>(),
2152-
StatementList::new_green(self.db, vec![]),
2184+
StatementList::new_green(self.db, vec![]).into(),
21532185
TerminalRBrace::missing(self.db),
21542186
);
21552187
}
21562188
// Don't report diagnostic if one has already been reported.
21572189
let lbrace = self.parse_token_ex::<TerminalLBrace>(skipped_tokens.is_ok());
2190+
match self.peek().kind {
2191+
SyntaxKind::TerminalDollar => {
2192+
let placeholder_repetition_block = self.parse_placeholder_repetition_block();
2193+
match placeholder_repetition_block {
2194+
Ok(placeholder_repetition_block) => {
2195+
let rbrace = self.parse_token::<TerminalRBrace>();
2196+
ExprBlock::new_green(self.db, lbrace, placeholder_repetition_block, rbrace)
2197+
}
2198+
Err(_) => self.parse_statements_and_block(lbrace),
2199+
}
2200+
}
2201+
_ => self.parse_statements_and_block(lbrace),
2202+
}
2203+
}
2204+
2205+
/// Parses a list of statements and constructs an `ExprBlockGreen`.
2206+
fn parse_statements_and_block(&mut self, lbrace: TerminalLBraceGreen) -> ExprBlockGreen {
21582207
let statements = StatementList::new_green(
21592208
self.db,
21602209
self.parse_list(
@@ -2164,7 +2213,7 @@ impl<'a> Parser<'a> {
21642213
),
21652214
);
21662215
let rbrace = self.parse_token::<TerminalRBrace>();
2167-
ExprBlock::new_green(self.db, lbrace, statements, rbrace)
2216+
ExprBlock::new_green(self.db, lbrace, statements.into(), rbrace)
21682217
}
21692218

21702219
/// Parses an expr block, while allowing placeholder expressions. Restores the previous
@@ -2593,22 +2642,41 @@ impl<'a> Parser<'a> {
25932642
.into(),
25942643
)
25952644
.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>(
2645+
SyntaxKind::TerminalLBrace => {
2646+
let expr_block = self.parse_block();
2647+
Ok(StatementExpr::new_green(
2648+
self.db,
26072649
attributes,
2608-
ParserDiagnosticKind::AttributesWithoutStatement,
2609-
)),
2610-
Err(err) => Err(err),
2611-
},
2650+
expr_block.into(),
2651+
OptionTerminalSemicolonEmpty::new_green(self.db).into(),
2652+
)
2653+
.into())
2654+
}
2655+
_ => self.parse_statement_expr(attributes, has_attrs),
2656+
}
2657+
}
2658+
2659+
/// Parses an expression and wraps it in a StatementExpr.
2660+
/// Handles optional semicolon and attributes.
2661+
fn parse_statement_expr(
2662+
&mut self,
2663+
attributes: AttributeListGreen,
2664+
has_attrs: bool,
2665+
) -> TryParseResult<StatementGreen> {
2666+
match self.try_parse_expr() {
2667+
Ok(expr) => {
2668+
let optional_semicolon = if self.peek().kind == SyntaxKind::TerminalSemicolon {
2669+
self.take::<TerminalSemicolon>().into()
2670+
} else {
2671+
OptionTerminalSemicolonEmpty::new_green(self.db).into()
2672+
};
2673+
Ok(StatementExpr::new_green(self.db, attributes, expr, optional_semicolon).into())
2674+
}
2675+
Err(_) if has_attrs => Ok(self.skip_taken_node_and_return_missing::<Statement>(
2676+
attributes,
2677+
ParserDiagnosticKind::AttributesWithoutStatement,
2678+
)),
2679+
Err(err) => Err(err),
26122680
}
26132681
}
26142682

@@ -2825,13 +2893,10 @@ impl<'a> Parser<'a> {
28252893
SyntaxKind::TerminalDollar => {
28262894
let dollar = self.take::<TerminalDollar>();
28272895
if !self.is_inside_macro_expansion {
2828-
self.add_diagnostic(
2829-
ParserDiagnosticKind::InvalidPlaceholderPath,
2830-
TextSpan {
2831-
start: self.offset,
2832-
end: self.offset.add_width(self.current_width),
2833-
},
2834-
)
2896+
self.add_diagnostic(ParserDiagnosticKind::InvalidPlaceholderPath, TextSpan {
2897+
start: self.offset,
2898+
end: self.offset.add_width(self.current_width),
2899+
})
28352900
};
28362901
dollar.into()
28372902
}
@@ -3259,10 +3324,10 @@ impl<'a> Parser<'a> {
32593324
if let (Some(diagnostic_kind), true) =
32603325
(forbid_trailing_separator, !children.is_empty())
32613326
{
3262-
self.add_diagnostic(
3263-
diagnostic_kind,
3264-
TextSpan { start: self.offset, end: self.offset },
3265-
);
3327+
self.add_diagnostic(diagnostic_kind, TextSpan {
3328+
start: self.offset,
3329+
end: self.offset,
3330+
});
32663331
}
32673332
break;
32683333
}

crates/cairo-lang-parser/src/parser_test_data/diagnostics/expr_diagnostics

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,7 @@ fn f() {
3636
}
3737

3838
//! > expected_diagnostics
39+
error: Skipped tokens. Expected: statement.
40+
--> dummy_file.cairo:4:7
41+
} + match x {
42+
^

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

Lines changed: 132 additions & 0 deletions
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_macro {
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_tree
293+
└── root (kind: SyntaxFile)
294+
├── items (kind: ModuleItemList)
295+
│ └── child #0 (kind: ItemMacroDeclaration)
296+
│ ├── attributes (kind: AttributeList) []
297+
│ ├── visibility (kind: VisibilityDefault) []
298+
│ ├── macro_kw (kind: TokenMacro): 'macro'
299+
│ ├── name (kind: TokenIdentifier): 'array_macro'
300+
│ ├── lbrace (kind: TokenLBrace): '{'
301+
│ ├── rules (kind: MacroRulesList)
302+
│ │ └── child #0 (kind: MacroRule)
303+
│ │ ├── lhs (kind: ParenthesizedMacroMatcher)
304+
│ │ │ ├── lparen (kind: TokenLParen): '('
305+
│ │ │ ├── elements (kind: MacroRuleElements)
306+
│ │ │ │ └── child #0 (kind: MacroRepetition)
307+
│ │ │ │ ├── dollar (kind: TokenDollar): '$'
308+
│ │ │ │ ├── lparen (kind: TokenLParen): '('
309+
│ │ │ │ ├── elements (kind: MacroRuleElements)
310+
│ │ │ │ │ └── child #0 (kind: MacroRuleParam)
311+
│ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
312+
│ │ │ │ │ ├── name (kind: TokenIdentifier): 'x'
313+
│ │ │ │ │ ├── colon (kind: TokenColon): ':'
314+
│ │ │ │ │ └── kind (kind: ParamExpr)
315+
│ │ │ │ │ └── expr (kind: TokenIdentifier): 'expr'
316+
│ │ │ │ ├── rparen (kind: TokenRParen): ')'
317+
│ │ │ │ ├── separator (kind: TokenComma): ','
318+
│ │ │ │ └── operator (kind: TokenMul): '*'
319+
│ │ │ └── rparen (kind: TokenRParen): ')'
320+
│ │ ├── fat_arrow (kind: TokenMatchArrow): '=>'
321+
│ │ ├── rhs (kind: ExprBlock)
322+
│ │ │ ├── lbrace (kind: TokenLBrace): '{'
323+
│ │ │ ├── statements (kind: StatementList)
324+
│ │ │ │ ├── child #0 (kind: StatementLet)
325+
│ │ │ │ │ ├── attributes (kind: AttributeList) []
326+
│ │ │ │ │ ├── let_kw (kind: TokenLet): 'let'
327+
│ │ │ │ │ ├── pattern (kind: PatternIdentifier)
328+
│ │ │ │ │ │ ├── modifiers (kind: ModifierList)
329+
│ │ │ │ │ │ │ └── child #0 (kind: TokenMut): 'mut'
330+
│ │ │ │ │ │ └── name (kind: TokenIdentifier): 'arr'
331+
│ │ │ │ │ ├── type_clause (kind: OptionTypeClauseEmpty) []
332+
│ │ │ │ │ ├── eq (kind: TokenEq): '='
333+
│ │ │ │ │ ├── rhs (kind: ExprFunctionCall)
334+
│ │ │ │ │ │ ├── path (kind: ExprPath)
335+
│ │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
336+
│ │ │ │ │ │ │ └── segments (kind: ExprPathInner)
337+
│ │ │ │ │ │ │ ├── item #0 (kind: PathSegmentSimple)
338+
│ │ │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'ArrayTrait'
339+
│ │ │ │ │ │ │ ├── separator #0 (kind: TokenColonColon): '::'
340+
│ │ │ │ │ │ │ └── item #1 (kind: PathSegmentSimple)
341+
│ │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'new'
342+
│ │ │ │ │ │ └── arguments (kind: ArgListParenthesized)
343+
│ │ │ │ │ │ ├── lparen (kind: TokenLParen): '('
344+
│ │ │ │ │ │ ├── arguments (kind: ArgList) []
345+
│ │ │ │ │ │ └── rparen (kind: TokenRParen): ')'
346+
│ │ │ │ │ └── semicolon (kind: TokenSemicolon): ';'
347+
│ │ │ │ ├── child #1 (kind: StatementExpr)
348+
│ │ │ │ │ ├── attributes (kind: AttributeList) []
349+
│ │ │ │ │ ├── expr (kind: ExprBlock)
350+
│ │ │ │ │ │ ├── lbrace (kind: TokenLBrace): '{'
351+
│ │ │ │ │ │ ├── statements (kind: PlaceholderRepetitionBlock)
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+
│ │ │ │ │ │ │ └── operator (kind: TokenMul): '*'
385+
│ │ │ │ │ │ └── rbrace (kind: TokenRBrace): '}'
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

0 commit comments

Comments
 (0)