Skip to content

Commit b1e23a2

Browse files
implemented ?
1 parent a38e8cb commit b1e23a2

File tree

8 files changed

+485
-53
lines changed

8 files changed

+485
-53
lines changed

corelib/src/test/language_features/macro_test.cairo

+34
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,37 @@ fn test_add_exprs() {
103103
assert_eq!(add_exprs!(abc 1 2), 3);
104104
assert_eq!(add_exprs!(0 2), 2);
105105
}
106+
107+
macro optional_repetitions {
108+
($x:ident) => {
109+
$x
110+
};
111+
112+
($x:ident {$y:ident }? {$z:ident }?) => {
113+
2
114+
};
115+
116+
($x:ident {$y:ident, {$z:ident }? }?) => {
117+
3
118+
};
119+
120+
($x:ident, $y:ident?) => {
121+
OptionTrait::unwrap_or(y, $x)
122+
};
123+
124+
($x:ident, $y:ident?, $z:ident?) => {
125+
OptionTrait::unwrap_or(z, OptionTrait::unwrap_or(y, $x))
126+
};
127+
}
128+
#[test]
129+
fn test_macro_optional_repetitions() {
130+
let x = 1;
131+
assert_eq!(optional_repetitions!(x), 1);
132+
let y: Option<felt252> = Some(2);
133+
assert_eq!(optional_repetitions!(x, y), 2);
134+
let z: Option<felt252> = Some(3);
135+
assert_eq!(optional_repetitions!(x, y, z), 3);
136+
assert_eq!(optional_repetitions!(x {y, {
137+
z
138+
}}), 3);
139+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ impl<'a> Parser<'a> {
701701
| SyntaxKind::TerminalLBrace
702702
| SyntaxKind::TerminalLBrack => {
703703
let subtree = self.parse_macro_elements();
704-
Ok(MacroMatcherwrapper::new_green(self.db, subtree).into())
704+
Ok(MacroMatcherWrapper::new_green(self.db, subtree).into())
705705
}
706706
_ => {
707707
let token = self.parse_token_tree_leaf();

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

+154-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ macro some_macro {
8181
│ │ │ │ │ └── ident (kind: TokenIdentifier): 'ident'
8282
│ │ │ │ ├── child #5 (kind: TokenTreeLeaf)
8383
│ │ │ │ │ └── leaf (kind: TokenPlus): '+'
84-
│ │ │ │ └── child #6 (kind: MacroMatcherwrapper)
84+
│ │ │ │ └── child #6 (kind: MacroMatcherWrapper)
8585
│ │ │ │ └── subtree (kind: ParenthesizedMacroMatcher)
8686
│ │ │ │ ├── lparen (kind: TokenLParen): '('
8787
│ │ │ │ ├── elements (kind: MacroRuleElements)
@@ -93,7 +93,7 @@ macro some_macro {
9393
│ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'ident'
9494
│ │ │ │ │ ├── child #1 (kind: TokenTreeLeaf)
9595
│ │ │ │ │ │ └── leaf (kind: TokenPlus): '+'
96-
│ │ │ │ │ └── child #2 (kind: MacroMatcherwrapper)
96+
│ │ │ │ │ └── child #2 (kind: MacroMatcherWrapper)
9797
│ │ │ │ │ └── subtree (kind: ParenthesizedMacroMatcher)
9898
│ │ │ │ │ ├── lparen (kind: TokenLParen): '('
9999
│ │ │ │ │ ├── elements (kind: MacroRuleElements)
@@ -118,3 +118,155 @@ macro some_macro {
118118
│ │ └── semicolon (kind: TokenSemicolon): ';'
119119
│ └── rbrace (kind: TokenRBrace): '}'
120120
└── eof (kind: TokenEndOfFile).
121+
122+
//! > ==========================================================================
123+
124+
//! > Test a syntax tree with optional repetitions
125+
126+
//! > test_runner_name
127+
test_partial_parser_tree(expect_diagnostics: false)
128+
129+
//! > cairo_code
130+
macro some_macro {
131+
($x:ident $y:ident? $z:expr?) => {
132+
1
133+
};
134+
}
135+
136+
fn use_macro() {
137+
some_macro!(foo);
138+
some_macro!(foo bar);
139+
some_macro!(foo bar 100);
140+
}
141+
142+
//! > top_level_kind
143+
144+
//! > ignored_kinds
145+
146+
//! > expected_diagnostics
147+
148+
//! > expected_tree
149+
└── root (kind: SyntaxFile)
150+
├── items (kind: ModuleItemList)
151+
│ ├── child #0 (kind: ItemMacroDeclaration)
152+
│ │ ├── attributes (kind: AttributeList) []
153+
│ │ ├── visibility (kind: VisibilityDefault) []
154+
│ │ ├── macro_kw (kind: TokenMacro): 'macro'
155+
│ │ ├── name (kind: TokenIdentifier): 'some_macro'
156+
│ │ ├── lbrace (kind: TokenLBrace): '{'
157+
│ │ ├── rules (kind: MacroRulesList)
158+
│ │ │ └── child #0 (kind: MacroRule)
159+
│ │ │ ├── lhs (kind: ParenthesizedMacroMatcher)
160+
│ │ │ │ ├── lparen (kind: TokenLParen): '('
161+
│ │ │ │ ├── elements (kind: MacroRuleElements)
162+
│ │ │ │ │ ├── child #0 (kind: MacroRuleParam)
163+
│ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
164+
│ │ │ │ │ │ ├── name (kind: TokenIdentifier): 'x'
165+
│ │ │ │ │ │ ├── colon (kind: TokenColon): ':'
166+
│ │ │ │ │ │ └── kind (kind: ParamIdent)
167+
│ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'ident'
168+
│ │ │ │ │ ├── child #1 (kind: MacroRuleParam)
169+
│ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
170+
│ │ │ │ │ │ ├── name (kind: TokenIdentifier): 'y'
171+
│ │ │ │ │ │ ├── colon (kind: TokenColon): ':'
172+
│ │ │ │ │ │ └── kind (kind: ParamIdent)
173+
│ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'ident'
174+
│ │ │ │ │ ├── child #2 (kind: TokenTreeLeaf)
175+
│ │ │ │ │ │ └── leaf (kind: TokenQuestionMark): '?'
176+
│ │ │ │ │ ├── child #3 (kind: MacroRuleParam)
177+
│ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
178+
│ │ │ │ │ │ ├── name (kind: TokenIdentifier): 'z'
179+
│ │ │ │ │ │ ├── colon (kind: TokenColon): ':'
180+
│ │ │ │ │ │ └── kind (kind: ParamExpr)
181+
│ │ │ │ │ │ └── expr (kind: TokenIdentifier): 'expr'
182+
│ │ │ │ │ └── child #4 (kind: TokenTreeLeaf)
183+
│ │ │ │ │ └── leaf (kind: TokenQuestionMark): '?'
184+
│ │ │ │ └── rparen (kind: TokenRParen): ')'
185+
│ │ │ ├── fat_arrow (kind: TokenMatchArrow): '=>'
186+
│ │ │ ├── rhs (kind: ExprBlock)
187+
│ │ │ │ ├── lbrace (kind: TokenLBrace): '{'
188+
│ │ │ │ ├── statements (kind: StatementList)
189+
│ │ │ │ │ └── child #0 (kind: StatementExpr)
190+
│ │ │ │ │ ├── attributes (kind: AttributeList) []
191+
│ │ │ │ │ ├── expr (kind: TokenLiteralNumber): '1'
192+
│ │ │ │ │ └── semicolon (kind: OptionTerminalSemicolonEmpty) []
193+
│ │ │ │ └── rbrace (kind: TokenRBrace): '}'
194+
│ │ │ └── semicolon (kind: TokenSemicolon): ';'
195+
│ │ └── rbrace (kind: TokenRBrace): '}'
196+
│ └── child #1 (kind: FunctionWithBody)
197+
│ ├── attributes (kind: AttributeList) []
198+
│ ├── visibility (kind: VisibilityDefault) []
199+
│ ├── declaration (kind: FunctionDeclaration)
200+
│ │ ├── optional_const (kind: OptionTerminalConstEmpty) []
201+
│ │ ├── function_kw (kind: TokenFunction): 'fn'
202+
│ │ ├── name (kind: TokenIdentifier): 'use_macro'
203+
│ │ ├── generic_params (kind: OptionWrappedGenericParamListEmpty) []
204+
│ │ └── signature (kind: FunctionSignature)
205+
│ │ ├── lparen (kind: TokenLParen): '('
206+
│ │ ├── parameters (kind: ParamList) []
207+
│ │ ├── rparen (kind: TokenRParen): ')'
208+
│ │ ├── ret_ty (kind: OptionReturnTypeClauseEmpty) []
209+
│ │ ├── implicits_clause (kind: OptionImplicitsClauseEmpty) []
210+
│ │ └── optional_no_panic (kind: OptionTerminalNoPanicEmpty) []
211+
│ └── body (kind: ExprBlock)
212+
│ ├── lbrace (kind: TokenLBrace): '{'
213+
│ ├── statements (kind: StatementList)
214+
│ │ ├── child #0 (kind: StatementExpr)
215+
│ │ │ ├── attributes (kind: AttributeList) []
216+
│ │ │ ├── expr (kind: ExprInlineMacro)
217+
│ │ │ │ ├── path (kind: ExprPath)
218+
│ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
219+
│ │ │ │ │ └── segments (kind: ExprPathInner)
220+
│ │ │ │ │ └── item #0 (kind: PathSegmentSimple)
221+
│ │ │ │ │ └── ident (kind: TokenIdentifier): 'some_macro'
222+
│ │ │ │ ├── bang (kind: TokenNot): '!'
223+
│ │ │ │ └── arguments (kind: TokenTreeNode)
224+
│ │ │ │ └── subtree (kind: ParenthesizedTokenTree)
225+
│ │ │ │ ├── lparen (kind: TokenLParen): '('
226+
│ │ │ │ ├── tokens (kind: TokenList)
227+
│ │ │ │ │ └── child #0 (kind: TokenTreeLeaf)
228+
│ │ │ │ │ └── leaf (kind: TokenIdentifier): 'foo'
229+
│ │ │ │ └── rparen (kind: TokenRParen): ')'
230+
│ │ │ └── semicolon (kind: TokenSemicolon): ';'
231+
│ │ ├── child #1 (kind: StatementExpr)
232+
│ │ │ ├── attributes (kind: AttributeList) []
233+
│ │ │ ├── expr (kind: ExprInlineMacro)
234+
│ │ │ │ ├── path (kind: ExprPath)
235+
│ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
236+
│ │ │ │ │ └── segments (kind: ExprPathInner)
237+
│ │ │ │ │ └── item #0 (kind: PathSegmentSimple)
238+
│ │ │ │ │ └── ident (kind: TokenIdentifier): 'some_macro'
239+
│ │ │ │ ├── bang (kind: TokenNot): '!'
240+
│ │ │ │ └── arguments (kind: TokenTreeNode)
241+
│ │ │ │ └── subtree (kind: ParenthesizedTokenTree)
242+
│ │ │ │ ├── lparen (kind: TokenLParen): '('
243+
│ │ │ │ ├── tokens (kind: TokenList)
244+
│ │ │ │ │ ├── child #0 (kind: TokenTreeLeaf)
245+
│ │ │ │ │ │ └── leaf (kind: TokenIdentifier): 'foo'
246+
│ │ │ │ │ └── child #1 (kind: TokenTreeLeaf)
247+
│ │ │ │ │ └── leaf (kind: TokenIdentifier): 'bar'
248+
│ │ │ │ └── rparen (kind: TokenRParen): ')'
249+
│ │ │ └── semicolon (kind: TokenSemicolon): ';'
250+
│ │ └── child #2 (kind: StatementExpr)
251+
│ │ ├── attributes (kind: AttributeList) []
252+
│ │ ├── expr (kind: ExprInlineMacro)
253+
│ │ │ ├── path (kind: ExprPath)
254+
│ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) []
255+
│ │ │ │ └── segments (kind: ExprPathInner)
256+
│ │ │ │ └── item #0 (kind: PathSegmentSimple)
257+
│ │ │ │ └── ident (kind: TokenIdentifier): 'some_macro'
258+
│ │ │ ├── bang (kind: TokenNot): '!'
259+
│ │ │ └── arguments (kind: TokenTreeNode)
260+
│ │ │ └── subtree (kind: ParenthesizedTokenTree)
261+
│ │ │ ├── lparen (kind: TokenLParen): '('
262+
│ │ │ ├── tokens (kind: TokenList)
263+
│ │ │ │ ├── child #0 (kind: TokenTreeLeaf)
264+
│ │ │ │ │ └── leaf (kind: TokenIdentifier): 'foo'
265+
│ │ │ │ ├── child #1 (kind: TokenTreeLeaf)
266+
│ │ │ │ │ └── leaf (kind: TokenIdentifier): 'bar'
267+
│ │ │ │ └── child #2 (kind: TokenTreeLeaf)
268+
│ │ │ │ └── leaf (kind: TokenLiteralNumber): '100'
269+
│ │ │ └── rparen (kind: TokenRParen): ')'
270+
│ │ └── semicolon (kind: TokenSemicolon): ';'
271+
│ └── rbrace (kind: TokenRBrace): '}'
272+
└── eof (kind: TokenEndOfFile).

crates/cairo-lang-semantic/src/items/macro_declaration.rs

+41-9
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,25 @@ fn is_macro_rule_match_ex(
124124
for matcher_element in matcher_elements.elements(db.upcast()) {
125125
match matcher_element {
126126
ast::MacroRuleElement::Token(matcher_token) => {
127-
let input_token = input_iter.next()?;
128-
match input_token {
129-
ast::TokenTree::Token(token_tree_leaf) => {
130-
if matcher_token.as_syntax_node().get_text_without_trivia(db.upcast())
131-
!= token_tree_leaf.as_syntax_node().get_text_without_trivia(db.upcast())
132-
{
133-
return None;
127+
if matcher_token.as_syntax_node().get_text_without_trivia(db.upcast()) == "?" {
128+
continue;
129+
}
130+
if let Some(input_token) = input_iter.next() {
131+
match input_token {
132+
ast::TokenTree::Token(token_tree_leaf) => {
133+
if matcher_token.as_syntax_node().get_text_without_trivia(db.upcast())
134+
!= token_tree_leaf
135+
.as_syntax_node()
136+
.get_text_without_trivia(db.upcast())
137+
{
138+
return None;
139+
}
134140
}
141+
ast::TokenTree::Subtree(_) => return None,
142+
ast::TokenTree::Missing(_) => unreachable!(),
135143
}
136-
ast::TokenTree::Subtree(_) => return None,
137-
ast::TokenTree::Missing(_) => unreachable!(),
144+
} else {
145+
return None;
138146
}
139147
}
140148
ast::MacroRuleElement::Param(param) => {
@@ -207,8 +215,32 @@ fn is_macro_rule_match_ex(
207215
ast::TokenTree::Missing(_) => unreachable!(),
208216
}
209217
}
218+
ast::MacroRuleElement::Repetition(repetition) => {
219+
let rep_op = repetition.operator(db.upcast());
220+
if rep_op.as_syntax_node().get_text_without_trivia(db.upcast()) == "?" {
221+
if let Some(next_input_token) = input_iter.next() {
222+
let mut temp_captures = OrderedHashMap::default();
223+
if is_macro_rule_match_ex(
224+
db,
225+
repetition.subtree(db.upcast()),
226+
match next_input_token {
227+
ast::TokenTree::Subtree(subtree) => subtree,
228+
_ => return None,
229+
},
230+
&mut temp_captures,
231+
)
232+
.is_some()
233+
{
234+
input_iter.next();
235+
captures.extend(temp_captures);
236+
}
237+
}
238+
continue;
239+
}
240+
}
210241
}
211242
}
243+
212244
if input_iter.next().is_some() {
213245
return None;
214246
}

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

+11-2
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,18 @@ pub fn get_spec() -> Vec<Node> {
902902
.add_enum(EnumBuilder::new("MacroRuleElement")
903903
.node_with_explicit_kind("Token", "TokenTreeLeaf")
904904
.node_with_explicit_kind("Param", "MacroRuleParam")
905-
.node_with_explicit_kind("Subtree", "MacroMatcherwrapper")
905+
.node_with_explicit_kind("Subtree", "MacroMatcherWrapper")
906+
.node_with_explicit_kind("Repetition", "MacroRepetitionWrapper")
906907
)
907-
.add_struct(StructBuilder::new("MacroMatcherwrapper")
908+
.add_struct(StructBuilder::new("MacroRepetitionWrapper")
909+
.node("subtree", "MacroMatcher")
910+
.node("operator", "MacroRepetitionOperatorWrapper")
911+
)
912+
.add_enum(EnumBuilder::new("MacroRepetitionOperatorWrapper")
913+
.node_with_explicit_kind("QuestionMark", "TerminalQuestionMark")
914+
// TODO(Dean): add Star & Plus.
915+
)
916+
.add_struct(StructBuilder::new("MacroMatcherWrapper")
908917
.node("subtree", "MacroMatcher")
909918
)
910919
.add_enum(EnumBuilder::new("MacroMatcher")

0 commit comments

Comments
 (0)