From 57a91318b26ef72923ac47a0047c286756fb3cac Mon Sep 17 00:00:00 2001 From: Gilad Chase Date: Sun, 26 Oct 2025 12:59:01 +0200 Subject: [PATCH] feat: Add &T to parser, semantic not implemented yet Note: change in `attribute_errors` are since `&` can now also be Unary, and the test wanted to test a missing binary operator, so `/` is now the binary operator used to keep the spirit of the test without changing the diff. --- .../src/node_properties.rs | 2 +- crates/cairo-lang-parser/src/operators.rs | 1 + crates/cairo-lang-parser/src/parser.rs | 6 ++ crates/cairo-lang-parser/src/parser_test.rs | 1 + .../partial_trees/logical_operator | 37 ++++++- .../parser_test_data/partial_trees/reference | 98 +++++++++++++++++++ .../attribute_errors | 18 ++-- crates/cairo-lang-semantic/src/corelib.rs | 1 + .../src/cairo_spec.rs | 1 + crates/cairo-lang-syntax/src/node/ast.rs | 19 ++++ 10 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 crates/cairo-lang-parser/src/parser_test_data/partial_trees/reference diff --git a/crates/cairo-lang-formatter/src/node_properties.rs b/crates/cairo-lang-formatter/src/node_properties.rs index efb24531e53..4b6fb1120b7 100644 --- a/crates/cairo-lang-formatter/src/node_properties.rs +++ b/crates/cairo-lang-formatter/src/node_properties.rs @@ -150,7 +150,7 @@ impl<'a> SyntaxNodeFormat for SyntaxNode<'a> { { true } - SyntaxKind::TokenMinus | SyntaxKind::TokenMul => { + SyntaxKind::TokenMinus | SyntaxKind::TokenMul | SyntaxKind::TokenAnd => { matches!(self.grandparent_kind(db), Some(SyntaxKind::ExprUnary)) } SyntaxKind::TokenPlus diff --git a/crates/cairo-lang-parser/src/operators.rs b/crates/cairo-lang-parser/src/operators.rs index 85000a15fab..231cea26d36 100644 --- a/crates/cairo-lang-parser/src/operators.rs +++ b/crates/cairo-lang-parser/src/operators.rs @@ -3,6 +3,7 @@ use cairo_lang_syntax::node::kind::SyntaxKind; pub fn get_unary_operator_precedence(kind: SyntaxKind) -> Option { match kind { SyntaxKind::TerminalAt + | SyntaxKind::TerminalAnd | SyntaxKind::TerminalNot | SyntaxKind::TerminalBitNot | SyntaxKind::TerminalMul diff --git a/crates/cairo-lang-parser/src/parser.rs b/crates/cairo-lang-parser/src/parser.rs index 297e21438f1..9643e1bd0cf 100644 --- a/crates/cairo-lang-parser/src/parser.rs +++ b/crates/cairo-lang-parser/src/parser.rs @@ -1503,6 +1503,7 @@ impl<'a, 'mt> Parser<'a, 'mt> { fn expect_unary_operator(&mut self) -> UnaryOperatorGreen<'a> { match self.peek().kind { SyntaxKind::TerminalAt => self.take::>().into(), + SyntaxKind::TerminalAnd => self.take::>().into(), SyntaxKind::TerminalNot => self.take::>().into(), SyntaxKind::TerminalBitNot => self.take::>().into(), SyntaxKind::TerminalMinus => self.take::>().into(), @@ -1706,6 +1707,11 @@ impl<'a, 'mt> Parser<'a, 'mt> { let expr = self.parse_type_expr(); Ok(ExprUnary::new_green(self.db, op, expr).into()) } + SyntaxKind::TerminalAnd => { + let op = self.take::>().into(); + let expr = self.parse_type_expr(); + Ok(ExprUnary::new_green(self.db, op, expr).into()) + } SyntaxKind::TerminalIdentifier | SyntaxKind::TerminalDollar => { Ok(self.parse_type_path().into()) } diff --git a/crates/cairo-lang-parser/src/parser_test.rs b/crates/cairo-lang-parser/src/parser_test.rs index 87180c2b6f3..86eaae6827b 100644 --- a/crates/cairo-lang-parser/src/parser_test.rs +++ b/crates/cairo-lang-parser/src/parser_test.rs @@ -199,6 +199,7 @@ cairo_lang_test_utils::test_file_test!( while_: "while", for_: "for", range: "range", + reference: "reference", use_: "use", type_alias: "type_alias", macro_declaration: "macro_declaration", diff --git a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/logical_operator b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/logical_operator index 6970156f2cc..951e6b0cfab 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/logical_operator +++ b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/logical_operator @@ -1,4 +1,4 @@ -//! > Test binary expression. +//! > Test logical AND and OR //! > test_runner_name test_partial_parser_tree(expect_diagnostics: false) @@ -42,3 +42,38 @@ ExprBinary └── segments (kind: ExprPathInner) └── item #0 (kind: PathSegmentSimple) └── ident (kind: TokenIdentifier): 'd' + +//! > ========================================================================== + +//! > Test reference type param with bitwise AND expression + +//! > test_runner_name +test_partial_parser_tree(expect_diagnostics: false) + +//! > cairo_code +fn foo(a: &u32, b: u32) { + a & &b +} + +//! > top_level_kind +ExprBinary + +//! > ignored_kinds + +//! > expected_diagnostics + +//! > expected_tree +└── Top level kind: ExprBinary + ├── lhs (kind: ExprPath) + │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ └── segments (kind: ExprPathInner) + │ └── item #0 (kind: PathSegmentSimple) + │ └── ident (kind: TokenIdentifier): 'a' + ├── op (kind: TokenAnd): '&' + └── rhs (kind: ExprUnary) + ├── op (kind: TokenAnd): '&' + └── expr (kind: ExprPath) + ├── dollar (kind: OptionTerminalDollarEmpty) [] + └── segments (kind: ExprPathInner) + └── item #0 (kind: PathSegmentSimple) + └── ident (kind: TokenIdentifier): 'b' diff --git a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/reference b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/reference new file mode 100644 index 00000000000..e2934f74765 --- /dev/null +++ b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/reference @@ -0,0 +1,98 @@ +//! > Test basic reference type + +//! > test_runner_name +test_partial_parser_tree(expect_diagnostics: false) + +//! > cairo_code +fn f(x: &u32) {} + +//! > top_level_kind +ExprUnary + +//! > ignored_kinds + +//! > expected_diagnostics + +//! > expected_tree +└── Top level kind: ExprUnary + ├── op (kind: TokenAnd): '&' + └── expr (kind: ExprPath) + ├── dollar (kind: OptionTerminalDollarEmpty) [] + └── segments (kind: ExprPathInner) + └── item #0 (kind: PathSegmentSimple) + └── ident (kind: TokenIdentifier): 'u32' + +//! > ========================================================================== + +//! > Test reference of snapshot vs snapshot of reference + +//! > test_runner_name +test_partial_parser_tree(expect_diagnostics: false) + +//! > cairo_code +fn f(x: &@u32, y: @ &u32) {} + +//! > top_level_kind +ParamList + +//! > ignored_kinds + +//! > expected_diagnostics + +//! > expected_tree +└── Top level kind: ParamList + ├── item #0 (kind: Param) + │ ├── modifiers (kind: ModifierList) [] + │ ├── name (kind: TokenIdentifier): 'x' + │ └── type_clause (kind: TypeClause) + │ ├── colon (kind: TokenColon): ':' + │ └── ty (kind: ExprUnary) + │ ├── op (kind: TokenAnd): '&' + │ └── expr (kind: ExprUnary) + │ ├── op (kind: TokenAt): '@' + │ └── expr (kind: ExprPath) + │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ └── segments (kind: ExprPathInner) + │ └── item #0 (kind: PathSegmentSimple) + │ └── ident (kind: TokenIdentifier): 'u32' + ├── separator #0 (kind: TokenComma): ',' + └── item #1 (kind: Param) + ├── modifiers (kind: ModifierList) [] + ├── name (kind: TokenIdentifier): 'y' + └── type_clause (kind: TypeClause) + ├── colon (kind: TokenColon): ':' + └── ty (kind: ExprUnary) + ├── op (kind: TokenAt): '@' + └── expr (kind: ExprUnary) + ├── op (kind: TokenAnd): '&' + └── expr (kind: ExprPath) + ├── dollar (kind: OptionTerminalDollarEmpty) [] + └── segments (kind: ExprPathInner) + └── item #0 (kind: PathSegmentSimple) + └── ident (kind: TokenIdentifier): 'u32' + +//! > ========================================================================== + +//! > Test reference inside generics + +//! > test_runner_name +test_partial_parser_tree(expect_diagnostics: false) + +//! > cairo_code +fn f() -> Option< &u64> {} + +//! > top_level_kind +ExprUnary + +//! > ignored_kinds + +//! > expected_diagnostics + +//! > expected_tree +└── Top level kind: ExprUnary + ├── op (kind: TokenAnd): '&' + └── expr (kind: ExprPath) + ├── dollar (kind: OptionTerminalDollarEmpty) [] + └── segments (kind: ExprPathInner) + └── item #0 (kind: PathSegmentSimple) + └── ident (kind: TokenIdentifier): 'u64' diff --git a/crates/cairo-lang-parser/src/parser_test_data/partial_trees_with_trivia/attribute_errors b/crates/cairo-lang-parser/src/parser_test_data/partial_trees_with_trivia/attribute_errors index 0eacdee0802..b70d2a67001 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/partial_trees_with_trivia/attribute_errors +++ b/crates/cairo-lang-parser/src/parser_test_data/partial_trees_with_trivia/attribute_errors @@ -978,7 +978,7 @@ test_partial_parser_tree_with_trivia(expect_diagnostics: true) //! > cairo_code fn foo() { #[aaa] - & + / } //! > top_level_kind @@ -1002,7 +1002,7 @@ error: Missing tokens. Expected a statement after attributes. error: Skipped tokens. Expected: statement. --> dummy_file.cairo:3:5 - & + / ^ //! > expected_tree @@ -1028,7 +1028,7 @@ error: Skipped tokens. Expected: statement. │ │ ├── arguments (kind: OptionArgListParenthesizedEmpty) [] │ │ └── rbrack (kind: TerminalRBrack) │ ├── child #1 (kind: TokenWhitespace). - │ ├── child #2 (kind: TokenSkipped): '&' + │ ├── child #2 (kind: TokenSkipped): '/' │ └── child #3 (kind: TokenNewline). ├── token (kind: TokenRBrace): '}' └── trailing_trivia (kind: Trivia) [] @@ -1042,7 +1042,7 @@ test_partial_parser_tree_with_trivia(expect_diagnostics: true) //! > cairo_code fn foo() { - & + / #[aaa] } @@ -1061,7 +1061,7 @@ TerminalSemicolon //! > expected_diagnostics error: Skipped tokens. Expected: statement. --> dummy_file.cairo:2:5 - & + / ^ error: Missing tokens. Expected a statement after attributes. @@ -1082,7 +1082,7 @@ error: Missing tokens. Expected a statement after attributes. │ ├── hash (kind: TerminalHash) │ │ ├── leading_trivia (kind: Trivia) │ │ │ ├── child #0 (kind: TokenWhitespace). - │ │ │ ├── child #1 (kind: TokenSkipped): '&' + │ │ │ ├── child #1 (kind: TokenSkipped): '/' │ │ │ ├── child #2 (kind: TokenNewline). │ │ │ └── child #3 (kind: TokenWhitespace). │ │ ├── token (kind: TokenHash): '#' @@ -1111,7 +1111,7 @@ test_partial_parser_tree_with_trivia(expect_diagnostics: true) //! > cairo_code fn foo() { #[aaa] - & + / #[bbb] } @@ -1135,7 +1135,7 @@ error: Missing tokens. Expected a statement after attributes. error: Skipped tokens. Expected: statement. --> dummy_file.cairo:3:5 - & + / ^ error: Missing tokens. Expected a statement after attributes. @@ -1176,7 +1176,7 @@ error: Missing tokens. Expected a statement after attributes. │ │ │ │ ├── arguments (kind: OptionArgListParenthesizedEmpty) [] │ │ │ │ └── rbrack (kind: TerminalRBrack) │ │ │ ├── child #1 (kind: TokenWhitespace). - │ │ │ ├── child #2 (kind: TokenSkipped): '&' + │ │ │ ├── child #2 (kind: TokenSkipped): '/' │ │ │ ├── child #3 (kind: TokenNewline). │ │ │ └── child #4 (kind: TokenWhitespace). │ │ ├── token (kind: TokenHash): '#' diff --git a/crates/cairo-lang-semantic/src/corelib.rs b/crates/cairo-lang-semantic/src/corelib.rs index 81c72c35e37..a3c1a5673dc 100644 --- a/crates/cairo-lang-semantic/src/corelib.rs +++ b/crates/cairo-lang-semantic/src/corelib.rs @@ -552,6 +552,7 @@ pub fn core_unary_operator<'db>( UnaryOperator::BitNot(_) => (info.bitnot_trt, info.bitnot_fn), UnaryOperator::At(_) => unreachable!("@ is not an unary operator."), UnaryOperator::Desnap(_) => unreachable!("* is not an unary operator."), + UnaryOperator::Reference(_) => unreachable!("& is handled before reaching here."), }; Ok(Ok(get_core_trait_function_infer(db, inference, trait_id, trait_fn, stable_ptr))) } diff --git a/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs b/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs index 9baa7721ff4..a341f7cb860 100644 --- a/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs +++ b/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs @@ -102,6 +102,7 @@ pub fn get_spec() -> Vec { .node_with_explicit_kind("Minus", "TerminalMinus") .node_with_explicit_kind("At", "TerminalAt") .node_with_explicit_kind("Desnap", "TerminalMul") + .node_with_explicit_kind("Reference", "TerminalAnd") ) .add_struct(StructBuilder::new("ExprBinary") .node("lhs", "Expr") diff --git a/crates/cairo-lang-syntax/src/node/ast.rs b/crates/cairo-lang-syntax/src/node/ast.rs index 4d442caa5dc..9678b94a10d 100644 --- a/crates/cairo-lang-syntax/src/node/ast.rs +++ b/crates/cairo-lang-syntax/src/node/ast.rs @@ -2706,6 +2706,7 @@ pub enum UnaryOperator<'db> { Minus(TerminalMinus<'db>), At(TerminalAt<'db>), Desnap(TerminalMul<'db>), + Reference(TerminalAnd<'db>), } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] pub struct UnaryOperatorPtr<'db>(pub SyntaxStablePtrId<'db>); @@ -2748,6 +2749,11 @@ impl<'db> From> for UnaryOperatorPtr<'db> { Self(value.0) } } +impl<'db> From> for UnaryOperatorPtr<'db> { + fn from(value: TerminalAndPtr<'db>) -> Self { + Self(value.0) + } +} impl<'db> From> for UnaryOperatorGreen<'db> { fn from(value: TerminalNotGreen<'db>) -> Self { Self(value.0) @@ -2773,6 +2779,11 @@ impl<'db> From> for UnaryOperatorGreen<'db> { Self(value.0) } } +impl<'db> From> for UnaryOperatorGreen<'db> { + fn from(value: TerminalAndGreen<'db>) -> Self { + Self(value.0) + } +} #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] pub struct UnaryOperatorGreen<'db>(pub GreenId<'db>); impl<'db> TypedSyntaxNode<'db> for UnaryOperator<'db> { @@ -2796,6 +2807,9 @@ impl<'db> TypedSyntaxNode<'db> for UnaryOperator<'db> { SyntaxKind::TerminalMul => { UnaryOperator::Desnap(TerminalMul::from_syntax_node(db, node)) } + SyntaxKind::TerminalAnd => { + UnaryOperator::Reference(TerminalAnd::from_syntax_node(db, node)) + } _ => panic!("Unexpected syntax kind {:?} when constructing {}.", kind, "UnaryOperator"), } } @@ -2817,6 +2831,9 @@ impl<'db> TypedSyntaxNode<'db> for UnaryOperator<'db> { SyntaxKind::TerminalMul => { Some(UnaryOperator::Desnap(TerminalMul::from_syntax_node(db, node))) } + SyntaxKind::TerminalAnd => { + Some(UnaryOperator::Reference(TerminalAnd::from_syntax_node(db, node))) + } _ => None, } } @@ -2827,6 +2844,7 @@ impl<'db> TypedSyntaxNode<'db> for UnaryOperator<'db> { UnaryOperator::Minus(x) => x.as_syntax_node(), UnaryOperator::At(x) => x.as_syntax_node(), UnaryOperator::Desnap(x) => x.as_syntax_node(), + UnaryOperator::Reference(x) => x.as_syntax_node(), } } fn stable_ptr(&self, db: &'db dyn Database) -> Self::StablePtr { @@ -2843,6 +2861,7 @@ impl<'db> UnaryOperator<'db> { | SyntaxKind::TerminalMinus | SyntaxKind::TerminalAt | SyntaxKind::TerminalMul + | SyntaxKind::TerminalAnd ) } }