diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index 4706be2df25..48b3b25747b 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -47,12 +47,17 @@ pub fn method_call( object, method_name: ident(method_name), arguments, + is_macro_call: false, generics: None, }))) } pub fn call(func: Expression, arguments: Vec) -> Expression { - expression(ExpressionKind::Call(Box::new(CallExpression { func: Box::new(func), arguments }))) + expression(ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(func), + is_macro_call: false, + arguments, + }))) } pub fn pattern(name: &str) -> Pattern { diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 50836add8de..da2d3646b20 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -33,7 +33,7 @@ pub enum ExpressionKind { Tuple(Vec), Lambda(Box), Parenthesized(Box), - Quote(BlockExpression), + Quote(BlockExpression, Span), Comptime(BlockExpression, Span), // This variant is only emitted when inlining the result of comptime @@ -179,19 +179,21 @@ impl Expression { pub fn member_access_or_method_call( lhs: Expression, - (rhs, args): UnaryRhsMemberAccess, + rhs: UnaryRhsMemberAccess, span: Span, ) -> Expression { - let kind = match args { - None => ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs, rhs })), - Some((generics, arguments)) => { - ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object: lhs, - method_name: rhs, - generics, - arguments, - })) + let kind = match rhs.method_call { + None => { + let rhs = rhs.method_or_field; + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs, rhs })) } + Some(method_call) => ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: lhs, + method_name: rhs.method_or_field, + generics: method_call.turbofish, + arguments: method_call.args, + is_macro_call: method_call.macro_call, + })), }; Expression::new(kind, span) } @@ -206,7 +208,12 @@ impl Expression { Expression::new(kind, span) } - pub fn call(lhs: Expression, arguments: Vec, span: Span) -> Expression { + pub fn call( + lhs: Expression, + is_macro_call: bool, + arguments: Vec, + span: Span, + ) -> Expression { // Need to check if lhs is an if expression since users can sequence if expressions // with tuples without calling them. E.g. `if c { t } else { e }(a, b)` is interpreted // as a sequence of { if, tuple } rather than a function call. This behavior matches rust. @@ -224,7 +231,11 @@ impl Expression { ], }) } else { - ExpressionKind::Call(Box::new(CallExpression { func: Box::new(lhs), arguments })) + ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(lhs), + is_macro_call, + arguments, + })) }; Expression::new(kind, span) } @@ -447,6 +458,7 @@ pub enum ArrayLiteral { pub struct CallExpression { pub func: Box, pub arguments: Vec, + pub is_macro_call: bool, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -456,6 +468,7 @@ pub struct MethodCallExpression { /// Method calls have an optional list of generics if the turbofish operator was used pub generics: Option>, pub arguments: Vec, + pub is_macro_call: bool, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -535,7 +548,7 @@ impl Display for ExpressionKind { } Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), - Quote(block) => write!(f, "quote {block}"), + Quote(block, _) => write!(f, "quote {block}"), Comptime(block, _) => write!(f, "comptime {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index bd2b45d9c48..c09ad75818a 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -134,8 +134,16 @@ pub struct UnresolvedType { } /// Type wrapper for a member access -pub(crate) type UnaryRhsMemberAccess = - (Ident, Option<(Option>, Vec)>); +pub struct UnaryRhsMemberAccess { + pub method_or_field: Ident, + pub method_call: Option, +} + +pub struct UnaryRhsMethodCall { + pub turbofish: Option>, + pub macro_call: bool, + pub args: Vec, +} /// The precursor to TypeExpression, this is the type that the parser allows /// to be used in the length position of an array type. Only constants, variables, diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 9b2c0fbfee8..57a62324064 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -608,6 +608,7 @@ impl ForRange { object: Expression::new(array_ident.clone(), array_span), method_name: Ident::new("len".to_string(), array_span), generics: None, + is_macro_call: false, arguments: vec![], })); let end_range = Expression::new(end_range, array_span); diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index c222e08e77a..f5866f5b756 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -581,6 +581,7 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta ), span, }), + is_macro_call: false, arguments: vec![uint_expr(var_id.0 as u128, span), expr], })); ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } @@ -599,6 +600,7 @@ fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement { ), span, }), + is_macro_call: false, arguments: vec![uint_expr(var_id.0 as u128, span)], })); ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } @@ -626,6 +628,7 @@ fn build_assign_member_stmt( ), span, }), + is_macro_call: false, arguments: [ vec![uint_expr(var_id.0 as u128, span)], vec![expr.clone()], @@ -649,6 +652,7 @@ fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Stat ), span, }), + is_macro_call: false, arguments: vec![uint_expr(fn_id.0 as u128, span)], })); ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index a922f552c4b..c1c6945707b 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -58,7 +58,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple), ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr), - ExpressionKind::Quote(quote) => self.elaborate_quote(quote), + ExpressionKind::Quote(quote, _) => self.elaborate_quote(quote), ExpressionKind::Comptime(comptime, _) => { return self.elaborate_comptime_block(comptime, expr.span) } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 01f58ba4c27..133f971da19 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1641,7 +1641,7 @@ impl<'a> Resolver<'a> { ExpressionKind::Parenthesized(sub_expr) => return self.resolve_expression(*sub_expr), // The quoted expression isn't resolved since we don't want errors if variables aren't defined - ExpressionKind::Quote(block) => HirExpression::Quote(block), + ExpressionKind::Quote(block, _) => HirExpression::Quote(block), ExpressionKind::Comptime(block, _) => { HirExpression::Comptime(self.resolve_block(block)) } diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index 2d1ebf530e3..3d052e22e36 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -141,6 +141,7 @@ impl<'a> Lexer<'a> { Some('}') => self.single_char_token(Token::RightBrace), Some('[') => self.single_char_token(Token::LeftBracket), Some(']') => self.single_char_token(Token::RightBracket), + Some('$') => self.single_char_token(Token::DollarSign), Some('"') => self.eat_string_literal(), Some('f') => self.eat_format_string_or_alpha_numeric(), Some('r') => self.eat_raw_string_or_alpha_numeric(), diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index d8555b4fbf7..2199333e90f 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -85,6 +85,8 @@ pub enum BorrowedToken<'input> { Semicolon, /// ! Bang, + /// $ + DollarSign, /// = Assign, #[allow(clippy::upper_case_acronyms)] @@ -179,6 +181,8 @@ pub enum Token { Bang, /// = Assign, + /// $ + DollarSign, #[allow(clippy::upper_case_acronyms)] EOF, @@ -238,6 +242,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::Semicolon => BorrowedToken::Semicolon, Token::Assign => BorrowedToken::Assign, Token::Bang => BorrowedToken::Bang, + Token::DollarSign => BorrowedToken::DollarSign, Token::EOF => BorrowedToken::EOF, Token::Invalid(c) => BorrowedToken::Invalid(*c), Token::Whitespace(ref s) => BorrowedToken::Whitespace(s), @@ -349,6 +354,7 @@ impl fmt::Display for Token { Token::Semicolon => write!(f, ";"), Token::Assign => write!(f, "="), Token::Bang => write!(f, "!"), + Token::DollarSign => write!(f, "$"), Token::EOF => write!(f, "end of input"), Token::Invalid(c) => write!(f, "{c}"), Token::Whitespace(ref s) => write!(f, "{s}"), @@ -840,6 +846,7 @@ pub enum Keyword { Crate, Dep, Else, + Expr, Field, Fn, For, @@ -884,6 +891,7 @@ impl fmt::Display for Keyword { Keyword::Crate => write!(f, "crate"), Keyword::Dep => write!(f, "dep"), Keyword::Else => write!(f, "else"), + Keyword::Expr => write!(f, "Expr"), Keyword::Field => write!(f, "Field"), Keyword::Fn => write!(f, "fn"), Keyword::For => write!(f, "for"), @@ -931,6 +939,7 @@ impl Keyword { "crate" => Keyword::Crate, "dep" => Keyword::Dep, "else" => Keyword::Else, + "Expr" => Keyword::Expr, "Field" => Keyword::Field, "fn" => Keyword::Fn, "for" => Keyword::For, diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index cabc788e07d..5107e10f4df 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -24,6 +24,7 @@ //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. use self::primitives::{keyword, mutable_reference, variable}; +use self::types::{generic_type_args, maybe_comp_time, parse_type}; use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, @@ -35,8 +36,8 @@ use super::{spanned, Item, ItemKind}; use crate::ast::{ BinaryOp, BinaryOpKind, BlockExpression, ForLoopStatement, ForRange, Ident, IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, NoirTypeAlias, Param, Path, Pattern, - Recoverable, Statement, TraitBound, TypeImpl, UnaryRhsMemberAccess, UnresolvedTraitConstraint, - UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, + Recoverable, Statement, TraitBound, TypeImpl, UnaryRhsMemberAccess, UnaryRhsMethodCall, + UnresolvedTraitConstraint, UseTree, UseTreeKind, Visibility, }; use crate::ast::{ Expression, ExpressionKind, LetStatement, StatementKind, UnresolvedType, UnresolvedTypeData, @@ -59,6 +60,7 @@ mod path; mod primitives; mod structs; mod traits; +mod types; // synthesized by LALRPOP lalrpop_mod!(pub noir_parser); @@ -674,41 +676,6 @@ where }) } -fn parse_type<'a>() -> impl NoirParser + 'a { - recursive(parse_type_inner) -} - -fn parse_type_inner<'a>( - recursive_type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - choice(( - field_type(), - int_type(), - bool_type(), - string_type(), - format_string_type(recursive_type_parser.clone()), - named_type(recursive_type_parser.clone()), - named_trait(recursive_type_parser.clone()), - slice_type(recursive_type_parser.clone()), - array_type(recursive_type_parser.clone()), - parenthesized_type(recursive_type_parser.clone()), - tuple_type(recursive_type_parser.clone()), - function_type(recursive_type_parser.clone()), - mutable_reference_type(recursive_type_parser), - )) -} - -fn parenthesized_type( - recursive_type_parser: impl NoirParser, -) -> impl NoirParser { - recursive_type_parser - .delimited_by(just(Token::LeftParen), just(Token::RightParen)) - .map_with_span(|typ, span| UnresolvedType { - typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), - span: span.into(), - }) -} - fn optional_visibility() -> impl NoirParser { keyword(Keyword::Pub) .or(keyword(Keyword::CallData)) @@ -724,187 +691,6 @@ fn optional_visibility() -> impl NoirParser { }) } -fn maybe_comp_time() -> impl NoirParser { - keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { - if opt.is_some() { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("Comptime values"), - span, - )); - } - opt.is_some() - }) -} - -fn field_type() -> impl NoirParser { - keyword(Keyword::Field) - .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) -} - -fn bool_type() -> impl NoirParser { - keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) -} - -fn string_type() -> impl NoirParser { - keyword(Keyword::String) - .ignore_then(type_expression().delimited_by(just(Token::Less), just(Token::Greater))) - .map_with_span(|expr, span| UnresolvedTypeData::String(expr).with_span(span)) -} - -fn format_string_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - keyword(Keyword::FormatString) - .ignore_then( - type_expression() - .then_ignore(just(Token::Comma)) - .then(type_parser) - .delimited_by(just(Token::Less), just(Token::Greater)), - ) - .map_with_span(|(size, fields), span| { - UnresolvedTypeData::FormatString(size, Box::new(fields)).with_span(span) - }) -} - -fn int_type() -> impl NoirParser { - filter_map(|span, token: Token| match token { - Token::IntType(int_type) => Ok(int_type), - unexpected => { - Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) - } - }) - .validate(|token, span, emit| { - UnresolvedTypeData::from_int_token(token).map(|data| data.with_span(span)).unwrap_or_else( - |err| { - emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); - UnresolvedType::error(span) - }, - ) - }) -} - -fn named_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - path().then(generic_type_args(type_parser)).map_with_span(|(path, args), span| { - UnresolvedTypeData::Named(path, args, false).with_span(span) - }) -} - -fn named_trait<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - keyword(Keyword::Impl).ignore_then(path()).then(generic_type_args(type_parser)).map_with_span( - |(path, args), span| UnresolvedTypeData::TraitAsType(path, args).with_span(span), - ) -} - -fn generic_type_args<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser> + 'a { - type_parser - .clone() - // Without checking for a terminating ',' or '>' here we may incorrectly - // parse a generic `N * 2` as just the type `N` then fail when there is no - // separator afterward. Failing early here ensures we try the `type_expression` - // parser afterward. - .then_ignore(one_of([Token::Comma, Token::Greater]).rewind()) - .or(type_expression() - .map_with_span(|expr, span| UnresolvedTypeData::Expression(expr).with_span(span))) - .separated_by(just(Token::Comma)) - .allow_trailing() - .at_least(1) - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not() - .map(Option::unwrap_or_default) -} - -fn array_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - just(Token::LeftBracket) - .ignore_then(type_parser) - .then(just(Token::Semicolon).ignore_then(type_expression())) - .then_ignore(just(Token::RightBracket)) - .map_with_span(|(element_type, size), span| { - UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) - }) -} - -fn slice_type(type_parser: impl NoirParser) -> impl NoirParser { - just(Token::LeftBracket) - .ignore_then(type_parser) - .then_ignore(just(Token::RightBracket)) - .map_with_span(|element_type, span| { - UnresolvedTypeData::Slice(Box::new(element_type)).with_span(span) - }) -} - -fn type_expression() -> impl NoirParser { - recursive(|expr| { - expression_with_precedence( - Precedence::lowest_type_precedence(), - expr, - nothing(), - nothing(), - true, - false, - ) - }) - .labelled(ParsingRuleLabel::TypeExpression) - .try_map(UnresolvedTypeExpression::from_expr) -} - -fn tuple_type(type_parser: T) -> impl NoirParser -where - T: NoirParser, -{ - let fields = type_parser.separated_by(just(Token::Comma)).allow_trailing(); - parenthesized(fields).map_with_span(|fields, span| { - if fields.is_empty() { - UnresolvedTypeData::Unit.with_span(span) - } else { - UnresolvedTypeData::Tuple(fields).with_span(span) - } - }) -} - -fn function_type(type_parser: T) -> impl NoirParser -where - T: NoirParser, -{ - let args = parenthesized(type_parser.clone().separated_by(just(Token::Comma)).allow_trailing()); - - let env = just(Token::LeftBracket) - .ignore_then(type_parser.clone()) - .then_ignore(just(Token::RightBracket)) - .or_not() - .map_with_span(|t, span| { - t.unwrap_or_else(|| UnresolvedTypeData::Unit.with_span(Span::empty(span.end()))) - }); - - keyword(Keyword::Fn) - .ignore_then(env) - .then(args) - .then_ignore(just(Token::Arrow)) - .then(type_parser) - .map_with_span(|((env, args), ret), span| { - UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env)).with_span(span) - }) -} - -fn mutable_reference_type(type_parser: T) -> impl NoirParser -where - T: NoirParser, -{ - just(Token::Ampersand) - .ignore_then(keyword(Keyword::Mut)) - .ignore_then(type_parser) - .map_with_span(|element, span| { - UnresolvedTypeData::MutableReference(Box::new(element)).with_span(span) - }) -} - fn expression() -> impl ExprParser { recursive(|expr| { expression_with_precedence( @@ -1073,14 +859,18 @@ where S: NoirParser + 'a, { enum UnaryRhs { - Call(Vec), + Call((Option, Vec)), ArrayIndex(Expression), Cast(UnresolvedType), MemberAccess(UnaryRhsMemberAccess), } // `(arg1, ..., argN)` in `my_func(arg1, ..., argN)` - let call_rhs = parenthesized(expression_list(expr_parser.clone())).map(UnaryRhs::Call); + // Optionally accepts a leading `!` for macro calls. + let call_rhs = just(Token::Bang) + .or_not() + .then(parenthesized(expression_list(expr_parser.clone()))) + .map(UnaryRhs::Call); // `[expr]` in `arr[expr]` let array_rhs = expr_parser @@ -1097,11 +887,23 @@ where // A turbofish operator is optional in a method call to specify generic types let turbofish = primitives::turbofish(type_parser); + // `::!(arg1, .., argN)` with the turbofish and macro portions being optional. + let method_call_rhs = turbofish + .then(just(Token::Bang).or_not()) + .then(parenthesized(expression_list(expr_parser.clone()))) + .map(|((turbofish, macro_call), args)| UnaryRhsMethodCall { + turbofish, + macro_call: macro_call.is_some(), + args, + }); + // `.foo` or `.foo(args)` in `atom.foo` or `atom.foo(args)` let member_rhs = just(Token::Dot) .ignore_then(field_name()) - .then(turbofish.then(parenthesized(expression_list(expr_parser.clone()))).or_not()) - .map(UnaryRhs::MemberAccess) + .then(method_call_rhs.or_not()) + .map(|(method_or_field, method_call)| { + UnaryRhs::MemberAccess(UnaryRhsMemberAccess { method_or_field, method_call }) + }) .labelled(ParsingRuleLabel::FieldAccess); let rhs = choice((call_rhs, array_rhs, cast_rhs, member_rhs)); @@ -1110,7 +912,9 @@ where atom(expr_parser, expr_no_constructors, statement, allow_constructors), rhs, |lhs, rhs, span| match rhs { - UnaryRhs::Call(args) => Expression::call(lhs, args, span), + UnaryRhs::Call((is_macro, args)) => { + Expression::call(lhs, is_macro.is_some(), args, span) + } UnaryRhs::ArrayIndex(index) => Expression::index(lhs, index, span), UnaryRhs::Cast(r#type) => Expression::cast(lhs, r#type, span), UnaryRhs::MemberAccess(field) => { @@ -1272,6 +1076,7 @@ where block(statement.clone()).map(ExpressionKind::Block), comptime_expr(statement.clone()), quote(statement), + unquote(expr_parser.clone()), variable(), literal(), )) @@ -1300,13 +1105,26 @@ fn quote<'a, P>(statement: P) -> impl NoirParser + 'a where P: NoirParser + 'a, { - keyword(Keyword::Quote).ignore_then(block(statement)).validate(|block, span, emit| { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("quoted expressions"), - span, - )); - ExpressionKind::Quote(block) - }) + keyword(Keyword::Quote).ignore_then(spanned(block(statement))).validate( + |(block, block_span), span, emit| { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("quoted expressions"), + span, + )); + ExpressionKind::Quote(block, block_span) + }, + ) +} + +/// unquote: '$' variable +/// | '$' '(' expression ')' +fn unquote<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + let unquote = variable().map_with_span(Expression::new).or(parenthesized(expr_parser)); + // This will be updated to ExpressionKind::Unquote in a later PR + just(Token::DollarSign).ignore_then(unquote).map(|_| ExpressionKind::Error) } fn tuple

(expr_parser: P) -> impl NoirParser @@ -1452,11 +1270,6 @@ mod test { ); } - #[test] - fn parse_type_expression() { - parse_all(type_expression(), vec!["(123)", "123", "(1 + 1)", "(1 + (1))"]); - } - #[test] fn parse_array_sugar() { let valid = vec!["[0;7]", "[(1, 2); 4]", "[0;Four]", "[2;1+3-a]"]; diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 82dd3dad681..a79a2ef67f2 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,22 +1,38 @@ use super::{ - expression_with_precedence, keyword, nothing, parenthesized, NoirParser, ParserError, + expression_with_precedence, keyword, nothing, parenthesized, path, NoirParser, ParserError, ParserErrorReason, Precedence, }; -use crate::ast::{UnresolvedType, UnresolvedTypeData}; +use crate::ast::{Recoverable, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}; use crate::parser::labels::ParsingRuleLabel; use crate::token::{Keyword, Token}; -use crate::{Recoverable, UnresolvedTypeExpression}; use chumsky::prelude::*; use noirc_errors::Span; -fn maybe_comp_time() -> impl NoirParser<()> { - keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { - if opt.is_some() { - emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); - } - }) +pub(super) fn parse_type<'a>() -> impl NoirParser + 'a { + recursive(parse_type_inner) +} + +pub(super) fn parse_type_inner<'a>( + recursive_type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + choice(( + field_type(), + int_type(), + bool_type(), + string_type(), + expr_type(), + format_string_type(recursive_type_parser.clone()), + named_type(recursive_type_parser.clone()), + named_trait(recursive_type_parser.clone()), + slice_type(recursive_type_parser.clone()), + array_type(recursive_type_parser.clone()), + parenthesized_type(recursive_type_parser.clone()), + tuple_type(recursive_type_parser.clone()), + function_type(recursive_type_parser.clone()), + mutable_reference_type(recursive_type_parser), + )) } pub(super) fn parenthesized_type( @@ -30,29 +46,41 @@ pub(super) fn parenthesized_type( }) } +pub(super) fn maybe_comp_time() -> impl NoirParser { + keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { + if opt.is_some() { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("Comptime values"), + span, + )); + } + opt.is_some() + }) +} + pub(super) fn field_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Field)) + keyword(Keyword::Field) .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) } pub(super) fn bool_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Bool)) - .map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) + keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) +} + +/// This is the type `Expr` - the type of a quoted, untyped expression object used for macros +pub(super) fn expr_type() -> impl NoirParser { + keyword(Keyword::Expr).map_with_span(|_, span| UnresolvedTypeData::Code.with_span(span)) } pub(super) fn string_type() -> impl NoirParser { keyword(Keyword::String) - .ignore_then( - type_expression().delimited_by(just(Token::Less), just(Token::Greater)).or_not(), - ) + .ignore_then(type_expression().delimited_by(just(Token::Less), just(Token::Greater))) .map_with_span(|expr, span| UnresolvedTypeData::String(expr).with_span(span)) } -pub(super) fn format_string_type( - type_parser: impl NoirParser, -) -> impl NoirParser { +pub(super) fn format_string_type<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { keyword(Keyword::FormatString) .ignore_then( type_expression() @@ -66,26 +94,61 @@ pub(super) fn format_string_type( } pub(super) fn int_type() -> impl NoirParser { - maybe_comp_time() - .then(filter_map(|span, token: Token| match token { - Token::IntType(int_type) => Ok(int_type), - unexpected => { - Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) - } - })) - .validate(|(_, token), span, emit| { - UnresolvedTypeData::from_int_token(token) - .map(|data| data.with_span(span)) - .unwrap_or_else(|err| { - emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); - UnresolvedType::error(span) - }) - }) + filter_map(|span, token: Token| match token { + Token::IntType(int_type) => Ok(int_type), + unexpected => { + Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) + } + }) + .validate(|token, span, emit| { + UnresolvedTypeData::from_int_token(token).map(|data| data.with_span(span)).unwrap_or_else( + |err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }, + ) + }) } -pub(super) fn array_type( - type_parser: impl NoirParser, -) -> impl NoirParser { +pub(super) fn named_type<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + path().then(generic_type_args(type_parser)).map_with_span(|(path, args), span| { + UnresolvedTypeData::Named(path, args, false).with_span(span) + }) +} + +pub(super) fn named_trait<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + keyword(Keyword::Impl).ignore_then(path()).then(generic_type_args(type_parser)).map_with_span( + |(path, args), span| UnresolvedTypeData::TraitAsType(path, args).with_span(span), + ) +} + +pub(super) fn generic_type_args<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser> + 'a { + type_parser + .clone() + // Without checking for a terminating ',' or '>' here we may incorrectly + // parse a generic `N * 2` as just the type `N` then fail when there is no + // separator afterward. Failing early here ensures we try the `type_expression` + // parser afterward. + .then_ignore(one_of([Token::Comma, Token::Greater]).rewind()) + .or(type_expression() + .map_with_span(|expr, span| UnresolvedTypeData::Expression(expr).with_span(span))) + .separated_by(just(Token::Comma)) + .allow_trailing() + .at_least(1) + .delimited_by(just(Token::Less), just(Token::Greater)) + .or_not() + .map(Option::unwrap_or_default) +} + +pub(super) fn array_type<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { just(Token::LeftBracket) .ignore_then(type_parser) .then(just(Token::Semicolon).ignore_then(type_expression())) diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 7ff943aea62..1d450bb7e28 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -63,7 +63,8 @@ pub(crate) fn rewrite( NewlineMode::IfContainsNewLineAndWidth, ); - format!("{callee}{args}") + let bang = if call_expr.is_macro_call { "!" } else { "" }; + format!("{callee}{bang}{args}") } ExpressionKind::MethodCall(method_call_expr) => { let args_span = visitor.span_before( @@ -85,7 +86,8 @@ pub(crate) fn rewrite( NewlineMode::IfContainsNewLineAndWidth, ); - format!("{object}.{method}{turbofish}{args}") + let bang = if method_call_expr.is_macro_call { "!" } else { "" }; + format!("{object}.{method}{turbofish}{bang}{args}") } ExpressionKind::MemberAccess(member_access_expr) => { let lhs_str = rewrite_sub_expr(visitor, shape, member_access_expr.lhs); @@ -166,7 +168,9 @@ pub(crate) fn rewrite( format!("{path_string}{turbofish}") } ExpressionKind::Lambda(_) => visitor.slice(span).to_string(), - ExpressionKind::Quote(block) => format!("quote {}", rewrite_block(visitor, block, span)), + ExpressionKind::Quote(block, block_span) => { + format!("quote {}", rewrite_block(visitor, block, block_span)) + } ExpressionKind::Comptime(block, block_span) => { format!("comptime {}", rewrite_block(visitor, block, block_span)) } diff --git a/tooling/nargo_fmt/tests/expected/let.nr b/tooling/nargo_fmt/tests/expected/let.nr index c57801155a0..7ff69e74306 100644 --- a/tooling/nargo_fmt/tests/expected/let.nr +++ b/tooling/nargo_fmt/tests/expected/let.nr @@ -44,12 +44,12 @@ fn let_() { } }; - let expr = Expr { + let expr = MyExpr { // A boolean literal (true, false). kind: ExprKind::Bool(true) }; - let expr = Expr { /*A boolean literal (true, false).*/ kind: ExprKind::Bool(true) }; + let expr = MyExpr { /*A boolean literal (true, false).*/ kind: ExprKind::Bool(true) }; let mut V = dep::crate2::MyStruct { Q: x }; let mut V = dep::crate2::MyStruct {}; diff --git a/tooling/nargo_fmt/tests/input/let.nr b/tooling/nargo_fmt/tests/input/let.nr index 67c4ab8bd52..37cdc6655c7 100644 --- a/tooling/nargo_fmt/tests/input/let.nr +++ b/tooling/nargo_fmt/tests/input/let.nr @@ -20,11 +20,11 @@ fn let_() { let person = Person { first_name: "John", last_name: "Doe", home_address: Address { street: "123 Main St", city: "Exampleville", zip_code: "12345", master: Person { first_name: "John", last_name: "Doe", home_address: Address { street: "123 Main St", city: "Exampleville", zip_code: "12345" } } } }; - let expr = Expr {// A boolean literal (true, false). + let expr = MyExpr {// A boolean literal (true, false). kind: ExprKind::Bool(true), }; - let expr = Expr {/*A boolean literal (true, false).*/kind: ExprKind::Bool(true),}; + let expr = MyExpr {/*A boolean literal (true, false).*/kind: ExprKind::Bool(true),}; let mut V = dep::crate2::MyStruct { Q: x }; let mut V = dep::crate2::MyStruct {};