diff --git a/Cargo.lock b/Cargo.lock index a5544310617..37ab577d32f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,17 +131,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.15", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -761,14 +750,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "chumsky" -version = "0.8.0" -source = "git+https://github.com/jfecher/chumsky?rev=ad9d312#ad9d312d9ffbc66c14514fa2b5752f4127b44f1e" -dependencies = [ - "hashbrown 0.11.2", -] - [[package]] name = "ciborium" version = "0.2.1" @@ -1830,15 +1811,6 @@ dependencies = [ "rayon", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1851,7 +1823,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.11", + "ahash", ] [[package]] @@ -2099,7 +2071,7 @@ version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" dependencies = [ - "ahash 0.8.11", + "ahash", "clap", "crossbeam-channel", "crossbeam-utils", @@ -2754,7 +2726,6 @@ version = "0.35.0" dependencies = [ "acvm", "async-lsp", - "chumsky", "codespan-lsp", "convert_case 0.6.0", "fm", @@ -2904,7 +2875,6 @@ version = "0.35.0" dependencies = [ "acvm", "base64 0.21.7", - "chumsky", "codespan", "codespan-reporting", "flate2", @@ -2947,7 +2917,6 @@ dependencies = [ "base64 0.21.7", "bn254_blackbox_solver", "cfg-if 1.0.0", - "chumsky", "fm", "im", "iter-extended", diff --git a/Cargo.toml b/Cargo.toml index 6741fb8a98e..09d266cbd5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,10 +117,6 @@ clap = { version = "4.3.19", features = ["derive", "env"] } codespan = { version = "0.11.1", features = ["serialization"] } codespan-lsp = "0.11.1" codespan-reporting = "0.11.1" -chumsky = { git = "https://github.com/jfecher/chumsky", rev = "ad9d312", default-features = false, features = [ - "ahash", - "std", -] } # Benchmarking criterion = "0.5.0" diff --git a/compiler/noirc_errors/Cargo.toml b/compiler/noirc_errors/Cargo.toml index 61b274c605f..a6927eb647a 100644 --- a/compiler/noirc_errors/Cargo.toml +++ b/compiler/noirc_errors/Cargo.toml @@ -16,7 +16,6 @@ acvm.workspace = true codespan-reporting.workspace = true codespan.workspace = true fm.workspace = true -chumsky.workspace = true noirc_printable_type.workspace = true serde.workspace = true serde_with = "3.2.0" diff --git a/compiler/noirc_errors/src/position.rs b/compiler/noirc_errors/src/position.rs index 9b031f56ae2..8131db323b9 100644 --- a/compiler/noirc_errors/src/position.rs +++ b/compiler/noirc_errors/src/position.rs @@ -8,7 +8,7 @@ use std::{ pub type Position = u32; -#[derive(PartialOrd, Eq, Ord, Debug, Clone)] +#[derive(PartialOrd, Eq, Ord, Debug, Clone, Default)] pub struct Spanned { pub contents: T, span: Span, @@ -121,26 +121,6 @@ impl From> for Span { } } -impl chumsky::Span for Span { - type Context = (); - - type Offset = u32; - - fn new(_context: Self::Context, range: Range) -> Self { - Span(ByteSpan::from(range)) - } - - fn context(&self) -> Self::Context {} - - fn start(&self) -> Self::Offset { - self.start() - } - - fn end(&self) -> Self::Offset { - self.end() - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Location { pub span: Span, diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index 510cff08dec..d729dabcb04 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -16,7 +16,6 @@ noirc_errors.workspace = true noirc_printable_type.workspace = true fm.workspace = true iter-extended.workspace = true -chumsky.workspace = true thiserror.workspace = true smol_str.workspace = true im.workspace = true diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 362f94171d3..64edae8322f 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -309,6 +309,7 @@ impl Expression { pub type BinaryOp = Spanned; #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)] +#[cfg_attr(test, derive(strum_macros::EnumIter))] pub enum BinaryOpKind { Add, Subtract, @@ -873,7 +874,7 @@ impl FunctionDefinition { impl Display for FunctionDefinition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{:?}", self.attributes)?; - write!(f, "fn {} {}", self.signature(), self.body) + write!(f, "{} {}", self.signature(), self.body) } } diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 23832277226..441eff99d9e 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -180,7 +180,7 @@ impl StatementKind { } } -#[derive(Eq, Debug, Clone)] +#[derive(Eq, Debug, Clone, Default)] pub struct Ident(pub Spanned); impl Ident { @@ -333,12 +333,12 @@ impl Display for UseTree { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.prefix)?; + if !self.prefix.segments.is_empty() { + write!(f, "::")?; + } + match &self.kind { UseTreeKind::Path(name, alias) => { - if !(self.prefix.segments.is_empty() && self.prefix.kind == PathKind::Plain) { - write!(f, "::")?; - } - write!(f, "{name}")?; if let Some(alias) = alias { @@ -348,7 +348,7 @@ impl Display for UseTree { Ok(()) } UseTreeKind::List(trees) => { - write!(f, "::{{")?; + write!(f, "{{")?; let tree = vecmap(trees, ToString::to_string).join(", "); write!(f, "{tree}}}") } @@ -467,7 +467,9 @@ impl Path { } pub fn is_ident(&self) -> bool { - self.segments.len() == 1 && self.kind == PathKind::Plain + self.kind == PathKind::Plain + && self.segments.len() == 1 + && self.segments.first().unwrap().generics.is_none() } pub fn as_ident(&self) -> Option<&Ident> { @@ -484,6 +486,10 @@ impl Path { self.segments.first().cloned().map(|segment| segment.ident) } + pub fn is_empty(&self) -> bool { + self.segments.is_empty() && self.kind == PathKind::Plain + } + pub fn as_string(&self) -> String { let mut string = String::new(); @@ -650,14 +656,6 @@ impl Pattern { } } - pub(crate) fn into_ident(self) -> Ident { - match self { - Pattern::Identifier(ident) => ident, - Pattern::Mutable(pattern, _, _) => pattern.into_ident(), - other => panic!("Pattern::into_ident called on {other} pattern with no identifier"), - } - } - pub(crate) fn try_as_expression(&self, interner: &NodeInterner) -> Option { match self { Pattern::Identifier(ident) => Some(Expression { @@ -726,37 +724,36 @@ impl LValue { Expression::new(kind, span) } - pub fn from_expression(expr: Expression) -> LValue { + pub fn from_expression(expr: Expression) -> Option { LValue::from_expression_kind(expr.kind, expr.span) } - pub fn from_expression_kind(expr: ExpressionKind, span: Span) -> LValue { + pub fn from_expression_kind(expr: ExpressionKind, span: Span) -> Option { match expr { - ExpressionKind::Variable(path) => LValue::Ident(path.as_ident().unwrap().clone()), - ExpressionKind::MemberAccess(member_access) => LValue::MemberAccess { - object: Box::new(LValue::from_expression(member_access.lhs)), + ExpressionKind::Variable(path) => Some(LValue::Ident(path.as_ident().unwrap().clone())), + ExpressionKind::MemberAccess(member_access) => Some(LValue::MemberAccess { + object: Box::new(LValue::from_expression(member_access.lhs)?), field_name: member_access.rhs, span, - }, - ExpressionKind::Index(index) => LValue::Index { - array: Box::new(LValue::from_expression(index.collection)), + }), + ExpressionKind::Index(index) => Some(LValue::Index { + array: Box::new(LValue::from_expression(index.collection)?), index: index.index, span, - }, + }), ExpressionKind::Prefix(prefix) => { if matches!( prefix.operator, crate::ast::UnaryOp::Dereference { implicitly_added: false } ) { - LValue::Dereference(Box::new(LValue::from_expression(prefix.rhs)), span) + Some(LValue::Dereference(Box::new(LValue::from_expression(prefix.rhs)?), span)) } else { - panic!("Called LValue::from_expression with an invalid prefix operator") + None } } - ExpressionKind::Interned(id) => LValue::Interned(id, span), - _ => { - panic!("Called LValue::from_expression with an invalid expression") - } + ExpressionKind::Parenthesized(expr) => LValue::from_expression(*expr), + ExpressionKind::Interned(id) => Some(LValue::Interned(id, span)), + _ => None, } } diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index d2fa95e4f5a..61ef6f6276d 100644 --- a/compiler/noirc_frontend/src/ast/traits.rs +++ b/compiler/noirc_frontend/src/ast/traits.rs @@ -216,7 +216,24 @@ impl Display for TraitBound { impl Display for NoirTraitImpl { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "impl {}{} for {} {{", self.trait_name, self.trait_generics, self.object_type)?; + write!(f, "impl")?; + if !self.impl_generics.is_empty() { + write!( + f, + "<{}>", + self.impl_generics.iter().map(ToString::to_string).collect::>().join(", ") + )?; + } + + write!(f, " {}{} for {}", self.trait_name, self.trait_generics, self.object_type)?; + if !self.where_clause.is_empty() { + write!( + f, + " where {}", + self.where_clause.iter().map(ToString::to_string).collect::>().join(", ") + )?; + } + writeln!(f, "{{")?; for item in self.items.iter() { let item = item.to_string(); diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 66de265f869..fed3149118b 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -1,5 +1,6 @@ use crate::ast::PathSegment; -use crate::parser::{parse_program, ParsedModule}; +use crate::parse_program; +use crate::parser::ParsedModule; use crate::{ ast, ast::{Path, PathKind}, diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 4d6095724f4..426e160206f 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -1,6 +1,5 @@ use std::{collections::BTreeMap, fmt::Display}; -use chumsky::Parser; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{Location, Span}; @@ -22,7 +21,7 @@ use crate::{ hir_def::expr::{HirExpression, HirIdent}, lexer::Lexer, node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, StructId, TraitId}, - parser::{self, TopLevelStatement, TopLevelStatementKind}, + parser::{Item, ItemKind, Parser}, token::SecondaryAttribute, Type, TypeBindings, UnificationError, }; @@ -261,9 +260,10 @@ impl<'context> Elaborator<'context> { return Err((lexing_errors.swap_remove(0).into(), location.file)); } - let expression = parser::expression() - .parse(tokens) - .map_err(|mut errors| (errors.swap_remove(0).into(), location.file))?; + let Some(expression) = Parser::for_tokens(tokens).parse_option(Parser::parse_expression) + else { + return Ok(None); + }; let (mut func, mut arguments) = match expression.kind { ExpressionKind::Call(call) => (*call.func, call.arguments), @@ -370,7 +370,7 @@ impl<'context> Elaborator<'context> { fn add_items( &mut self, - items: Vec, + items: Vec, generated_items: &mut CollectedItems, location: Location, ) { @@ -381,12 +381,12 @@ impl<'context> Elaborator<'context> { pub(crate) fn add_item( &mut self, - item: TopLevelStatement, + item: Item, generated_items: &mut CollectedItems, location: Location, ) { match item.kind { - TopLevelStatementKind::Function(function) => { + ItemKind::Function(function) => { let module_id = self.module_id(); if let Some(id) = dc_mod::collect_function( @@ -407,7 +407,7 @@ impl<'context> Elaborator<'context> { }); } } - TopLevelStatementKind::TraitImpl(mut trait_impl) => { + ItemKind::TraitImpl(mut trait_impl) => { let (methods, associated_types, associated_constants) = dc_mod::collect_trait_impl_items( self.interner, @@ -437,7 +437,7 @@ impl<'context> Elaborator<'context> { resolved_trait_generics: Vec::new(), }); } - TopLevelStatementKind::Global(global, visibility) => { + ItemKind::Global(global, visibility) => { let (global, error) = dc_mod::collect_global( self.interner, self.def_maps.get_mut(&self.crate_id).unwrap(), @@ -453,7 +453,7 @@ impl<'context> Elaborator<'context> { self.errors.push(error); } } - TopLevelStatementKind::Struct(struct_def) => { + ItemKind::Struct(struct_def) => { if let Some((type_id, the_struct)) = dc_mod::collect_struct( self.interner, self.def_maps.get_mut(&self.crate_id).unwrap(), @@ -466,20 +466,17 @@ impl<'context> Elaborator<'context> { generated_items.types.insert(type_id, the_struct); } } - TopLevelStatementKind::Impl(r#impl) => { + ItemKind::Impl(r#impl) => { let module = self.module_id(); dc_mod::collect_impl(self.interner, generated_items, r#impl, self.file, module); } - // Assume that an error has already been issued - TopLevelStatementKind::Error => (), - - TopLevelStatementKind::Module(_) - | TopLevelStatementKind::Import(..) - | TopLevelStatementKind::Trait(_) - | TopLevelStatementKind::TypeAlias(_) - | TopLevelStatementKind::SubModule(_) - | TopLevelStatementKind::InnerAttribute(_) => { + ItemKind::ModuleDecl(_) + | ItemKind::Import(..) + | ItemKind::Trait(_) + | ItemKind::TypeAlias(_) + | ItemKind::Submodules(_) + | ItemKind::InnerAttribute(_) => { let item = item.kind.to_string(); let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location }; self.errors.push(error.into_compilation_error_pair()); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 0b028217c46..7d3c95efdcb 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -10,7 +10,6 @@ use builtin_helpers::{ mutate_func_meta_type, parse, quote_ident, replace_func_meta_parameters, replace_func_meta_return_type, }; -use chumsky::{chain::Chain, prelude::choice, primitive::just, Parser}; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; @@ -33,12 +32,10 @@ use crate::{ def_collector::dc_crate::CollectedItems, def_map::ModuleDefId, }, - hir_def::{ - expr::{HirExpression, HirLiteral}, - function::FunctionBody, - }, + hir_def::expr::{HirExpression, HirLiteral}, + hir_def::function::FunctionBody, node_interner::{DefinitionKind, NodeInterner, TraitImplKind}, - parser, + parser::Parser, token::{Attribute, SecondaryAttribute, Token}, Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, }; @@ -675,15 +672,24 @@ fn quoted_as_expr( ) -> IResult { let argument = check_one_argument(arguments, location)?; - let expr_parser = parser::expression().map(|expr| Value::expression(expr.kind)); - let statement_parser = parser::fresh_statement().map(Value::statement); - let lvalue_parser = parser::lvalue(parser::expression()).map(Value::lvalue); - let parser = choice((expr_parser, statement_parser, lvalue_parser)); - let parser = parser.then_ignore(just(Token::Semicolon).or_not()); + let result = + parse(interner, argument.clone(), Parser::parse_expression_or_error, "an expression"); + if let Ok(expr) = result { + return option(return_type, Some(Value::expression(expr.kind))); + } - let expr = parse(interner, argument, parser, "an expression").ok(); + let result = + parse(interner, argument.clone(), Parser::parse_statement_or_error, "an expression"); + if let Ok(stmt) = result { + return option(return_type, Some(Value::statement(stmt.kind))); + } - option(return_type, expr) + let result = parse(interner, argument, Parser::parse_lvalue_or_error, "an expression"); + if let Ok(lvalue) = result { + return option(return_type, Some(Value::lvalue(lvalue))); + } + + option(return_type, None) } // fn as_module(quoted: Quoted) -> Option @@ -695,9 +701,13 @@ fn quoted_as_module( ) -> IResult { let argument = check_one_argument(arguments, location)?; - let path = - parse(interpreter.elaborator.interner, argument, parser::path_no_turbofish(), "a path") - .ok(); + let path = parse( + interpreter.elaborator.interner, + argument, + Parser::parse_path_no_turbofish_or_error, + "a path", + ) + .ok(); let option_value = path.and_then(|path| { let module = interpreter .elaborate_in_function(interpreter.current_function, |elaborator| { @@ -719,7 +729,7 @@ fn quoted_as_trait_constraint( let trait_bound = parse( interpreter.elaborator.interner, argument, - parser::trait_bound(), + Parser::parse_trait_bound_or_error, "a trait constraint", )?; let bound = interpreter @@ -738,7 +748,8 @@ fn quoted_as_type( location: Location, ) -> IResult { let argument = check_one_argument(arguments, location)?; - let typ = parse(interpreter.elaborator.interner, argument, parser::parse_type(), "a type")?; + let typ = + parse(interpreter.elaborator.interner, argument, Parser::parse_type_or_error, "a type")?; let typ = interpreter .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); Ok(Value::Type(typ)) @@ -2325,7 +2336,7 @@ fn function_def_set_parameters( let parameter_pattern = parse( interpreter.elaborator.interner, (tuple.pop().unwrap(), parameters_argument_location), - parser::pattern(), + Parser::parse_pattern_or_error, "a pattern", )?; @@ -2426,7 +2437,7 @@ fn module_add_item( let module_id = get_module(self_argument)?; let module_data = interpreter.elaborator.get_module(module_id); - let parser = parser::top_level_items(); + let parser = Parser::parse_top_level_items; let top_level_statements = parse(interpreter.elaborator.interner, item, parser, "a top-level item")?; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index a355b23b74f..3f9d92cfe88 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -5,7 +5,9 @@ use acvm::FieldElement; use noirc_errors::Location; use crate::hir::comptime::display::tokens_to_string; +use crate::hir::comptime::value::add_token_spans; use crate::lexer::Lexer; +use crate::parser::Parser; use crate::{ ast::{ BlockExpression, ExpressionKind, Ident, IntegerBitSize, LValue, Pattern, Signedness, @@ -14,7 +16,7 @@ use crate::{ hir::{ comptime::{ errors::IResult, - value::{add_token_spans, ExprValue, TypedExpr}, + value::{ExprValue, TypedExpr}, Interpreter, InterpreterError, Value, }, def_map::ModuleId, @@ -25,7 +27,6 @@ use crate::{ stmt::HirPattern, }, node_interner::{FuncId, NodeInterner, StructId, TraitId, TraitImplId}, - parser::NoirParser, token::{SecondaryAttribute, Token, Tokens}, QuotedType, Type, }; @@ -402,27 +403,32 @@ pub(super) fn lex(input: &str) -> Vec { tokens } -pub(super) fn parse( +pub(super) fn parse<'a, T, F>( interner: &NodeInterner, (value, location): (Value, Location), - parser: impl NoirParser, + parser: F, rule: &'static str, -) -> IResult { - let parser = parser.then_ignore(chumsky::primitive::end()); +) -> IResult +where + F: FnOnce(&mut Parser<'a>) -> T, +{ let tokens = get_quoted((value, location))?; let quoted = add_token_spans(tokens.clone(), location.span); parse_tokens(tokens, quoted, interner, location, parser, rule) } -pub(super) fn parse_tokens( +pub(super) fn parse_tokens<'a, T, F>( tokens: Rc>, quoted: Tokens, interner: &NodeInterner, location: Location, - parser: impl NoirParser, + parsing_function: F, rule: &'static str, -) -> IResult { - parser.parse(quoted).map_err(|mut errors| { +) -> IResult +where + F: FnOnce(&mut Parser<'a>) -> T, +{ + Parser::for_tokens(quoted).parse_result(parsing_function).map_err(|mut errors| { let error = errors.swap_remove(0); let tokens = tokens_to_string(tokens, interner); InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file } diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index 310c4a78414..458a186a3f8 100644 --- a/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -14,7 +14,7 @@ use crate::hir::def_collector::dc_crate::DefCollector; use crate::hir::def_collector::dc_mod::collect_defs; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData}; use crate::hir::{Context, ParsedFiles}; -use crate::parser::parse_program; +use crate::parse_program; fn interpret_helper(src: &str) -> Result { let file = FileId::default(); diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 4c968234f04..4b55735fcb1 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -1,7 +1,6 @@ use std::{borrow::Cow, rc::Rc, vec}; use acvm::{AcirField, FieldElement}; -use chumsky::Parser; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::{Location, Span}; @@ -19,7 +18,7 @@ use crate::{ ImplKind, }, node_interner::{ExprId, FuncId, NodeInterner, StmtId, StructId, TraitId, TraitImplId}, - parser::{self, NoirParser, TopLevelStatement}, + parser::{Item, Parser}, token::{SpannedToken, Token, Tokens}, Kind, QuotedType, Shared, Type, TypeBindings, }; @@ -261,7 +260,8 @@ impl Value { tokens_to_parse.0.insert(0, SpannedToken::new(Token::LeftBrace, location.span)); tokens_to_parse.0.push(SpannedToken::new(Token::RightBrace, location.span)); - return match parser::expression().parse(tokens_to_parse) { + let parser = Parser::for_tokens(tokens_to_parse); + return match parser.parse_result(Parser::parse_expression_or_error) { Ok(expr) => Ok(expr), Err(mut errors) => { let error = errors.swap_remove(0); @@ -523,8 +523,8 @@ impl Value { self, location: Location, interner: &NodeInterner, - ) -> IResult> { - let parser = parser::top_level_items(); + ) -> IResult> { + let parser = Parser::parse_top_level_items; match self { Value::Quoted(tokens) => { parse_tokens(tokens, interner, parser, location, "top-level item") @@ -543,15 +543,18 @@ pub(crate) fn unwrap_rc(rc: Rc) -> T { Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()) } -fn parse_tokens( +fn parse_tokens<'a, T, F>( tokens: Rc>, interner: &NodeInterner, - parser: impl NoirParser, + parsing_function: F, location: Location, rule: &'static str, -) -> IResult { - let parser = parser.then_ignore(chumsky::primitive::end()); - match parser.parse(add_token_spans(tokens.clone(), location.span)) { +) -> IResult +where + F: FnOnce(&mut Parser<'a>) -> T, +{ + let parser = Parser::for_tokens(add_token_spans(tokens.clone(), location.span)); + match parser.parse_result(parsing_function) { Ok(expr) => Ok(expr), Err(mut errors) => { let error = errors.swap_remove(0); diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index e9dfc5ac067..60ee2c52842 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -2,7 +2,8 @@ use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; use crate::node_interner::{FuncId, GlobalId, NodeInterner, StructId}; -use crate::parser::{parse_program, ParsedModule, ParserError}; +use crate::parse_program; +use crate::parser::{ParsedModule, ParserError}; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; use fm::{FileId, FileManager}; use noirc_arena::{Arena, Index}; diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index b8c98428bb0..5c2322acfda 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -244,6 +244,7 @@ impl std::fmt::Display for Kind { } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] +#[cfg_attr(test, derive(strum_macros::EnumIter))] pub enum QuotedType { Expr, Quoted, diff --git a/compiler/noirc_frontend/src/lexer/errors.rs b/compiler/noirc_frontend/src/lexer/errors.rs index 6e64c509195..66f79bd444b 100644 --- a/compiler/noirc_frontend/src/lexer/errors.rs +++ b/compiler/noirc_frontend/src/lexer/errors.rs @@ -143,10 +143,3 @@ impl<'a> From<&'a LexerErrorKind> for Diagnostic { Diagnostic::simple_error(primary, secondary, span) } } - -impl From for chumsky::error::Simple { - fn from(error: LexerErrorKind) -> Self { - let (_, message, span) = error.parts(); - chumsky::error::Simple::custom(span, message) - } -} diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 626c46d392f..d79a184d4c4 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -1,6 +1,6 @@ use acvm::{acir::AcirField, FieldElement}; use noirc_errors::{Position, Span, Spanned}; -use std::{fmt, iter::Map, vec::IntoIter}; +use std::fmt; use crate::{ lexer::errors::LexerErrorKind, @@ -1227,25 +1227,6 @@ impl Keyword { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Tokens(pub Vec); -type TokenMapIter = Map, fn(SpannedToken) -> (Token, Span)>; - -impl<'a> From for chumsky::Stream<'a, Token, Span, TokenMapIter> { - fn from(tokens: Tokens) -> Self { - let end_of_input = match tokens.0.last() { - Some(spanned_token) => spanned_token.to_span(), - None => Span::single_char(0), - }; - - fn get_span(token: SpannedToken) -> (Token, Span) { - let span = token.to_span(); - (token.into_token(), span) - } - - let iter = tokens.0.into_iter().map(get_span as fn(_) -> _); - chumsky::Stream::from_iter(end_of_input, iter) - } -} - #[cfg(test)] mod keywords { use strum::IntoEnumIterator; diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 6a7096c10c2..b80c37c2ce4 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -2145,6 +2145,7 @@ impl NodeInterner { pub fn get_lvalue(&self, id: InternedExpressionKind, span: Span) -> LValue { LValue::from_expression_kind(self.get_expression_kind(id).clone(), span) + .expect("Called LValue::from_expression with an invalid expression") } pub fn push_pattern(&mut self, pattern: Pattern) -> InternedPattern { diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 75fe1bf747f..f9cc539d7b7 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -1,4 +1,4 @@ -use crate::ast::{Expression, IntegerBitSize}; +use crate::ast::{Expression, IntegerBitSize, ItemVisibility}; use crate::lexer::errors::LexerErrorKind; use crate::lexer::token::Token; use crate::token::TokenKind; @@ -13,26 +13,50 @@ use super::labels::ParsingRuleLabel; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum ParserErrorReason { - #[error("Unexpected '{0}', expected a field name")] + #[error("Unexpected `;`")] + UnexpectedSemicolon, + #[error("Unexpected `,`")] + UnexpectedComma, + #[error("Expected a `{token}` separating these two {items}")] + ExpectedTokenSeparatingTwoItems { token: Token, items: &'static str }, + #[error("Invalid left-hand side of assignment")] + InvalidLeftHandSideOfAssignment, + #[error("Expected trait, found {found}")] + ExpectedTrait { found: String }, + #[error("Visibility `{visibility}` is not followed by an item")] + VisibilityNotFollowedByAnItem { visibility: ItemVisibility }, + #[error("`unconstrained` is not followed by an item")] + UnconstrainedNotFollowedByAnItem, + #[error("`comptime` is not followed by an item")] + ComptimeNotFollowedByAnItem, + #[error("`mut` cannot be applied to this item")] + MutableNotApplicable, + #[error("`comptime` cannot be applied to this item")] + ComptimeNotApplicable, + #[error("`unconstrained` cannot be applied to this item")] + UnconstrainedNotApplicable, + #[error("Expected an identifier or `(expression) after `$` for unquoting")] + ExpectedIdentifierOrLeftParenAfterDollar, + #[error("`&mut` can only be used with `self")] + RefMutCanOnlyBeUsedWithSelf, + #[error("Invalid pattern")] + InvalidPattern, + #[error("Documentation comment does not document anything")] + DocCommentDoesNotDocumentAnything, + + #[error("Missing type for function parameter")] + MissingTypeForFunctionParameter, + #[error("Missing type for numeric generic")] + MissingTypeForNumericGeneric, + #[error("Expected a function body (`{{ ... }}`), not `;`")] + ExpectedFunctionBody, + #[error("Expected the global to have a value")] + GlobalWithoutValue, + + #[error("Unexpected '{0}', expected a field name or number")] ExpectedFieldName(Token), - #[error("expected a pattern but found a type - {0}")] + #[error("Expected a pattern but found a type - {0}")] ExpectedPatternButFoundType(Token), - #[error("expected an identifier after .")] - ExpectedIdentifierAfterDot, - #[error("expected an identifier after ::")] - ExpectedIdentifierAfterColons, - #[error("expected {{ or -> after function parameters")] - ExpectedLeftBraceOrArrowAfterFunctionParameters, - #[error("expected {{ after if condition")] - ExpectedLeftBraceAfterIfCondition, - #[error("expected <, where or {{ after trait name")] - ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitName, - #[error("expected <, where or {{ after impl type")] - ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterImplType, - #[error("expected <, where or {{ after trait impl for type")] - ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, - #[error("expected ( or < after function name")] - ExpectedLeftParenOrLeftBracketAfterFunctionName, #[error("Expected a ; separating these two statements")] MissingSeparatingSemi, #[error("constrain keyword is deprecated")] @@ -105,6 +129,20 @@ impl ParserError { } } + pub fn expected_token(token: Token, found: Token, span: Span) -> ParserError { + let mut error = ParserError::empty(found, span); + error.expected_tokens.insert(token); + error + } + + pub fn expected_one_of_tokens(tokens: &[Token], found: Token, span: Span) -> ParserError { + let mut error = ParserError::empty(found, span); + for token in tokens { + error.expected_tokens.insert(token.clone()); + } + error + } + pub fn expected_label(label: ParsingRuleLabel, found: Token, span: Span) -> ParserError { let mut error = ParserError::empty(found, span); error.expected_labels.insert(label); @@ -206,7 +244,7 @@ impl<'a> From<&'a ParserError> for Diagnostic { Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) } ParserErrorReason::ExpectedPatternButFoundType(ty) => Diagnostic::simple_error( - "Expected a ; separating these two statements".into(), + format!("Expected a pattern but found a type - {ty}"), format!("{ty} is a type and cannot be used as a variable name"), error.span, ), @@ -229,45 +267,3 @@ impl<'a> From<&'a ParserError> for Diagnostic { } } } - -impl chumsky::Error for ParserError { - type Span = Span; - type Label = ParsingRuleLabel; - - fn expected_input_found(span: Self::Span, expected: Iter, found: Option) -> Self - where - Iter: IntoIterator>, - { - ParserError { - expected_tokens: expected.into_iter().map(|opt| opt.unwrap_or(Token::EOF)).collect(), - expected_labels: SmallOrdSet::new(), - found: found.unwrap_or(Token::EOF), - reason: None, - span, - } - } - - fn with_label(mut self, label: Self::Label) -> Self { - self.expected_tokens.clear(); - self.expected_labels.clear(); - self.expected_labels.insert(label); - self - } - - // Merge two errors into a new one that should encompass both. - // If one error has a more specific reason with it then keep - // that reason and discard the other if present. - // The spans of both errors must match, otherwise the error - // messages and error spans may not line up. - fn merge(mut self, mut other: Self) -> Self { - self.expected_tokens.append(&mut other.expected_tokens); - self.expected_labels.append(&mut other.expected_labels); - - if self.reason.is_none() { - self.reason = other.reason; - } - - self.span = self.span.merge(other.span); - self - } -} diff --git a/compiler/noirc_frontend/src/parser/labels.rs b/compiler/noirc_frontend/src/parser/labels.rs index fd082dfbe56..5c9ec236e07 100644 --- a/compiler/noirc_frontend/src/parser/labels.rs +++ b/compiler/noirc_frontend/src/parser/labels.rs @@ -11,14 +11,26 @@ pub enum ParsingRuleLabel { Cast, Expression, FieldAccess, + GenericParameter, Global, + Identifier, + Integer, IntegerType, + Item, + LValue, Parameter, + Path, Pattern, Statement, Term, + TraitBound, + TraitImplItem, + TraitItem, + Type, TypeExpression, + TypeOrTypeExpression, TokenKind(TokenKind), + UseSegment, } impl fmt::Display for ParsingRuleLabel { @@ -29,14 +41,26 @@ impl fmt::Display for ParsingRuleLabel { ParsingRuleLabel::Cast => write!(f, "cast"), ParsingRuleLabel::Expression => write!(f, "expression"), ParsingRuleLabel::FieldAccess => write!(f, "field access"), + ParsingRuleLabel::GenericParameter => write!(f, "generic parameter"), ParsingRuleLabel::Global => write!(f, "global"), + ParsingRuleLabel::Identifier => write!(f, "identifier"), + ParsingRuleLabel::Integer => write!(f, "integer"), ParsingRuleLabel::IntegerType => write!(f, "integer type"), + ParsingRuleLabel::Item => write!(f, "item"), + ParsingRuleLabel::LValue => write!(f, "left-hand side of assignment"), ParsingRuleLabel::Parameter => write!(f, "parameter"), + ParsingRuleLabel::Path => write!(f, "path"), ParsingRuleLabel::Pattern => write!(f, "pattern"), ParsingRuleLabel::Statement => write!(f, "statement"), ParsingRuleLabel::Term => write!(f, "term"), + ParsingRuleLabel::TraitBound => write!(f, "trait bound"), + ParsingRuleLabel::TraitImplItem => write!(f, "trait impl item"), + ParsingRuleLabel::TraitItem => write!(f, "trait item"), + ParsingRuleLabel::Type => write!(f, "type"), ParsingRuleLabel::TypeExpression => write!(f, "type expression"), - ParsingRuleLabel::TokenKind(token_kind) => write!(f, "{token_kind:?}"), + ParsingRuleLabel::TypeOrTypeExpression => write!(f, "type or type expression"), + ParsingRuleLabel::TokenKind(token_kind) => write!(f, "{token_kind}"), + ParsingRuleLabel::UseSegment => write!(f, "identifier, `crate`, `dep` or `super`"), } } } diff --git a/compiler/noirc_frontend/src/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index ad6dd1459e9..21c182a52cd 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -12,233 +12,15 @@ mod labels; mod parser; use crate::ast::{ - Documented, Expression, Ident, ImportStatement, ItemVisibility, LetStatement, - ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, - Recoverable, StatementKind, TypeImpl, UseTree, + Documented, Ident, ImportStatement, ItemVisibility, LetStatement, ModuleDeclaration, + NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TypeImpl, UseTree, }; -use crate::token::{Keyword, SecondaryAttribute, Token}; +use crate::token::SecondaryAttribute; -use chumsky::prelude::*; -use chumsky::primitive::Container; pub use errors::ParserError; pub use errors::ParserErrorReason; use noirc_errors::Span; -pub use parser::path::path_no_turbofish; -pub use parser::traits::trait_bound; -pub use parser::{ - block, expression, fresh_statement, lvalue, module, parse_program, parse_type, pattern, - top_level_items, top_level_statement, visibility, -}; - -#[derive(Debug, Clone)] -pub struct TopLevelStatement { - pub kind: TopLevelStatementKind, - pub doc_comments: Vec, -} - -#[derive(Debug, Clone)] -pub enum TopLevelStatementKind { - Function(NoirFunction), - Module(ModuleDeclaration), - Import(UseTree, ItemVisibility), - Struct(NoirStruct), - Trait(NoirTrait), - TraitImpl(NoirTraitImpl), - Impl(TypeImpl), - TypeAlias(NoirTypeAlias), - SubModule(ParsedSubModule), - Global(LetStatement, ItemVisibility), - InnerAttribute(SecondaryAttribute), - Error, -} - -impl TopLevelStatementKind { - pub fn into_item_kind(self) -> Option { - match self { - TopLevelStatementKind::Function(f) => Some(ItemKind::Function(f)), - TopLevelStatementKind::Module(m) => Some(ItemKind::ModuleDecl(m)), - TopLevelStatementKind::Import(i, visibility) => Some(ItemKind::Import(i, visibility)), - TopLevelStatementKind::Struct(s) => Some(ItemKind::Struct(s)), - TopLevelStatementKind::Trait(t) => Some(ItemKind::Trait(t)), - TopLevelStatementKind::TraitImpl(t) => Some(ItemKind::TraitImpl(t)), - TopLevelStatementKind::Impl(i) => Some(ItemKind::Impl(i)), - TopLevelStatementKind::TypeAlias(t) => Some(ItemKind::TypeAlias(t)), - TopLevelStatementKind::SubModule(s) => Some(ItemKind::Submodules(s)), - TopLevelStatementKind::Global(c, visibility) => Some(ItemKind::Global(c, visibility)), - TopLevelStatementKind::InnerAttribute(a) => Some(ItemKind::InnerAttribute(a)), - TopLevelStatementKind::Error => None, - } - } -} - -// Helper trait that gives us simpler type signatures for return types: -// e.g. impl Parser versus impl Parser> -pub trait NoirParser: Parser + Sized + Clone {} -impl NoirParser for P where P: Parser + Clone {} - -// ExprParser just serves as a type alias for NoirParser + Clone -pub trait ExprParser: NoirParser {} -impl

ExprParser for P where P: NoirParser {} - -fn parenthesized(parser: P) -> impl NoirParser -where - P: NoirParser, - T: Recoverable, -{ - use Token::*; - parser.delimited_by(just(LeftParen), just(RightParen)).recover_with(nested_delimiters( - LeftParen, - RightParen, - [(LeftBracket, RightBracket)], - Recoverable::error, - )) -} - -fn spanned(parser: P) -> impl NoirParser<(T, Span)> -where - P: NoirParser, -{ - parser.map_with_span(|value, span| (value, span)) -} - -// Parse with the first parser, then continue by -// repeating the second parser 0 or more times. -// The passed in function is then used to combine the -// results of both parsers along with their spans at -// each step. -fn foldl_with_span( - first_parser: P1, - to_be_repeated: P2, - f: F, -) -> impl NoirParser -where - P1: NoirParser, - P2: NoirParser, - F: Fn(T1, T2, Span) -> T1 + Clone, -{ - spanned(first_parser) - .then(spanned(to_be_repeated).repeated()) - .foldl(move |(a, a_span), (b, b_span)| { - let span = a_span.merge(b_span); - (f(a, b, span), span) - }) - .map(|(value, _span)| value) -} - -/// Sequence the two parsers. -/// Fails if the first parser fails, otherwise forces -/// the second parser to succeed while logging any errors. -fn then_commit<'a, P1, P2, T1, T2>( - first_parser: P1, - second_parser: P2, -) -> impl NoirParser<(T1, T2)> + 'a -where - P1: NoirParser + 'a, - P2: NoirParser + 'a, - T2: Clone + Recoverable + 'a, -{ - let second_parser = skip_then_retry_until(second_parser) - .map_with_span(|option, span| option.unwrap_or_else(|| Recoverable::error(span))); - - first_parser.then(second_parser) -} - -fn then_commit_ignore<'a, P1, P2, T1, T2>( - first_parser: P1, - second_parser: P2, -) -> impl NoirParser + 'a -where - P1: NoirParser + 'a, - P2: NoirParser + 'a, - T1: 'a, - T2: Clone + 'a, -{ - let second_parser = skip_then_retry_until(second_parser); - first_parser.then_ignore(second_parser) -} - -fn ignore_then_commit<'a, P1, P2, T1: 'a, T2: Clone + 'a>( - first_parser: P1, - second_parser: P2, -) -> impl NoirParser + 'a -where - P1: NoirParser + 'a, - P2: NoirParser + 'a, - T2: Recoverable, -{ - let second_parser = skip_then_retry_until(second_parser) - .map_with_span(|option, span| option.unwrap_or_else(|| Recoverable::error(span))); - - first_parser.ignore_then(second_parser) -} - -fn skip_then_retry_until<'a, P, T>(parser: P) -> impl NoirParser> + 'a -where - P: NoirParser + 'a, - T: Clone + 'a, -{ - let terminators = [ - Token::EOF, - Token::Colon, - Token::Semicolon, - Token::RightBrace, - Token::Keyword(Keyword::Let), - Token::Keyword(Keyword::Constrain), - ]; - force(parser.recover_with(chumsky::prelude::skip_then_retry_until(terminators))) -} - -/// General recovery strategy: try to skip to the target token, failing if we encounter the -/// 'too_far' token beforehand. -/// -/// Expects all of `too_far` to be contained within `targets` -fn try_skip_until(targets: C1, too_far: C2) -> impl NoirParser -where - T: Recoverable + Clone, - C1: Container + Clone, - C2: Container + Clone, -{ - chumsky::prelude::none_of(targets) - .repeated() - .ignore_then(one_of(too_far.clone()).rewind()) - .try_map(move |peek, span| { - if too_far.get_iter().any(|t| t == peek) { - // This error will never be shown to the user - Err(ParserError::empty(Token::EOF, span)) - } else { - Ok(Recoverable::error(span)) - } - }) -} - -/// Recovery strategy for statements: If a statement fails to parse skip until the next ';' or fail -/// if we find a '}' first. -fn statement_recovery() -> impl NoirParser { - use Token::*; - try_skip_until([Semicolon, RightBrace], RightBrace) -} - -fn parameter_recovery() -> impl NoirParser { - use Token::*; - try_skip_until([Comma, RightParen], RightParen) -} - -fn parameter_name_recovery() -> impl NoirParser { - use Token::*; - try_skip_until([Colon, RightParen, Comma], [RightParen, Comma]) -} - -fn top_level_statement_recovery() -> impl NoirParser { - none_of([Token::RightBrace, Token::EOF]) - .repeated() - .ignore_then(one_of([Token::Semicolon])) - .map(|_| TopLevelStatementKind::Error) -} - -/// Force the given parser to succeed, logging any errors it had -fn force<'a, T: 'a>(parser: impl NoirParser + 'a) -> impl NoirParser> + 'a { - parser.map(Some).recover_via(empty().map(|_| None)) -} +pub use parser::{parse_program, Parser}; #[derive(Clone, Default)] pub struct SortedModule { @@ -362,6 +144,35 @@ pub enum ItemKind { InnerAttribute(SecondaryAttribute), } +impl std::fmt::Display for ItemKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ItemKind::Function(fun) => fun.fmt(f), + ItemKind::ModuleDecl(m) => m.fmt(f), + ItemKind::Import(tree, visibility) => { + if visibility == &ItemVisibility::Private { + write!(f, "use {tree}") + } else { + write!(f, "{visibility} use {tree}") + } + } + ItemKind::Trait(t) => t.fmt(f), + ItemKind::TraitImpl(i) => i.fmt(f), + ItemKind::Struct(s) => s.fmt(f), + ItemKind::Impl(i) => i.fmt(f), + ItemKind::TypeAlias(t) => t.fmt(f), + ItemKind::Submodules(s) => s.fmt(f), + ItemKind::Global(c, visibility) => { + if visibility != &ItemVisibility::Private { + write!(f, "{visibility} ")?; + } + c.fmt(f) + } + ItemKind::InnerAttribute(a) => write!(f, "#![{}]", a), + } + } +} + /// A submodule defined via `mod name { contents }` in some larger file. /// These submodules always share the same file as some larger ParsedModule #[derive(Clone, Debug)] @@ -453,112 +264,6 @@ impl SortedModule { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)] -pub enum Precedence { - Lowest, - Or, - And, - Xor, - LessGreater, - Shift, - Sum, - Product, - Highest, -} - -impl Precedence { - // Higher the number, the higher(more priority) the precedence - // XXX: Check the precedence is correct for operators - fn token_precedence(tok: &Token) -> Option { - let precedence = match tok { - Token::Equal => Precedence::Lowest, - Token::NotEqual => Precedence::Lowest, - Token::Pipe => Precedence::Or, - Token::Ampersand => Precedence::And, - Token::Caret => Precedence::Xor, - Token::Less => Precedence::LessGreater, - Token::LessEqual => Precedence::LessGreater, - Token::Greater => Precedence::LessGreater, - Token::GreaterEqual => Precedence::LessGreater, - Token::ShiftLeft => Precedence::Shift, - Token::ShiftRight => Precedence::Shift, - Token::Plus => Precedence::Sum, - Token::Minus => Precedence::Sum, - Token::Slash => Precedence::Product, - Token::Star => Precedence::Product, - Token::Percent => Precedence::Product, - _ => return None, - }; - - assert_ne!(precedence, Precedence::Highest, "expression_with_precedence in the parser currently relies on the highest precedence level being uninhabited"); - Some(precedence) - } - - /// Return the next higher precedence. E.g. `Sum.next() == Product` - fn next(self) -> Self { - use Precedence::*; - match self { - Lowest => Or, - Or => Xor, - Xor => And, - And => LessGreater, - LessGreater => Shift, - Shift => Sum, - Sum => Product, - Product => Highest, - Highest => Highest, - } - } - - /// TypeExpressions only contain basic arithmetic operators and - /// notably exclude `>` due to parsing conflicts with generic type brackets. - fn next_type_precedence(self) -> Self { - use Precedence::*; - match self { - Lowest => Sum, - Sum => Product, - Product => Highest, - Highest => Highest, - other => unreachable!("Unexpected precedence level in type expression: {:?}", other), - } - } - - /// The operators with the lowest precedence still useable in type expressions - /// are '+' and '-' with precedence Sum. - fn lowest_type_precedence() -> Self { - Precedence::Sum - } -} - -impl std::fmt::Display for TopLevelStatementKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TopLevelStatementKind::Function(fun) => fun.fmt(f), - TopLevelStatementKind::Module(m) => m.fmt(f), - TopLevelStatementKind::Import(tree, visibility) => { - if visibility != &ItemVisibility::Private { - write!(f, "{visibility} ")?; - } - write!(f, "use {tree}") - } - TopLevelStatementKind::Trait(t) => t.fmt(f), - TopLevelStatementKind::TraitImpl(i) => i.fmt(f), - TopLevelStatementKind::Struct(s) => s.fmt(f), - TopLevelStatementKind::Impl(i) => i.fmt(f), - TopLevelStatementKind::TypeAlias(t) => t.fmt(f), - TopLevelStatementKind::SubModule(s) => s.fmt(f), - TopLevelStatementKind::Global(c, visibility) => { - if visibility != &ItemVisibility::Private { - write!(f, "{visibility} ")?; - } - c.fmt(f) - } - TopLevelStatementKind::InnerAttribute(a) => write!(f, "#![{}]", a), - TopLevelStatementKind::Error => write!(f, "error"), - } - } -} - impl std::fmt::Display for ParsedModule { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.clone().into_sorted().fmt(f) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 60128c6cafe..d0b0579ce24 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,88 +1,41 @@ -//! This file contains the bulk of the implementation of noir's parser. -//! -//! Noir's parser is built off the [chumsky library](https://docs.rs/chumsky/latest/chumsky/) -//! for parser combinators. In this technique, parsers are built from smaller parsers that -//! parse e.g. only a single token. Then there are functions which can combine multiple -//! parsers together to create a larger one. These functions are called parser combinators. -//! For example, `a.then(b)` combines two parsers a and b and returns one that parses a -//! then parses b and fails if either fails. Other combinators like `a.or(b)` exist as well -//! and are used extensively. Note that these form a PEG grammar so if there are multiple -//! options as in `a.or(b)` the first matching parse will be chosen. -//! -//! Noir's grammar is not formally specified but can be estimated by inspecting each function. -//! For example, a function `f` parsing `choice((a, b, c))` can be roughly translated to -//! BNF as `f: a | b | c`. -//! -//! Occasionally there will also be recovery strategies present, either via `recover_via(Parser)` -//! or `recover_with(Strategy)`. The difference between the two functions isn't quite so important, -//! but both allow the parser to recover from a parsing error, log the error, and return an error -//! expression instead. These are used to parse cases such as `fn foo( { }` where we know the user -//! meant to write a function and thus we should log the error and return a function with no parameters, -//! rather than failing to parse a function and trying to subsequently parse a struct. Generally using -//! recovery strategies improves parser errors but using them incorrectly can be problematic since they -//! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should -//! 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::path::{as_trait_path, type_path}; -use self::primitives::{ - interned_statement, interned_statement_expr, keyword, macro_quote_marker, mutable_reference, - variable, +use acvm::FieldElement; +use modifiers::Modifiers; +use noirc_errors::Span; + +use crate::{ + ast::{Ident, ItemVisibility, LValue}, + lexer::{Lexer, SpannedTokenResult}, + token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; -use self::types::{generic_type_args, maybe_comp_time}; -use attributes::{attributes, inner_attribute, validate_secondary_attributes}; -use doc_comments::{inner_doc_comments, outer_doc_comments}; -use types::interned_unresolved_type; -pub use types::parse_type; -use visibility::item_visibility; -pub use visibility::visibility; - -use super::{ - foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, - parenthesized, then_commit, then_commit_ignore, top_level_statement_recovery, ExprParser, - NoirParser, ParsedModule, ParsedSubModule, ParserError, ParserErrorReason, Precedence, - TopLevelStatementKind, -}; -use super::{spanned, Item, TopLevelStatement}; -use crate::ast::{ - BinaryOp, BinaryOpKind, BlockExpression, Documented, ForLoopStatement, ForRange, - GenericTypeArgs, Ident, IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, - NoirTypeAlias, Param, Path, Pattern, Recoverable, Statement, TypeImpl, UnaryRhsMemberAccess, - UnaryRhsMethodCall, UseTree, UseTreeKind, Visibility, -}; -use crate::ast::{ - Expression, ExpressionKind, LetStatement, StatementKind, UnresolvedType, UnresolvedTypeData, -}; -use crate::lexer::Lexer; -use crate::parser::{force, ignore_then_commit, statement_recovery}; -use crate::token::{Keyword, Token, TokenKind}; -use acvm::AcirField; -use chumsky::prelude::*; -use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReason}; -mod assertion; +mod arguments; mod attributes; mod doc_comments; +mod expression; mod function; -mod lambdas; -mod literals; -pub(super) mod path; -mod primitives; +mod generics; +mod global; +mod impls; +mod infix; +mod item; +mod item_visibility; +mod lambda; +mod modifiers; +mod module; +mod parse_many; +mod path; +mod pattern; +mod statement; mod structs; -pub(super) mod traits; +mod tests; +mod traits; +mod type_alias; +mod type_expression; mod types; -mod visibility; - -#[cfg(test)] -mod test_helpers; - -use literals::literal; -use path::{maybe_empty_path, path}; -use primitives::{ - dereference, ident, interned_expr, negation, not, nothing, right_shift_operator, token_kind, -}; -use traits::where_clause; +mod use_tree; +mod where_clause; /// Entry function for the parser - also handles lexing internally. /// @@ -91,1764 +44,513 @@ use traits::where_clause; /// Vec is non-empty, there may be Error nodes in the Ast to fill in the gaps that /// failed to parse. Otherwise the Ast is guaranteed to have 0 Error nodes. pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { - let (tokens, lexing_errors) = Lexer::lex(source_program); - let (module, mut parsing_errors) = program().parse_recovery_verbose(tokens); - - parsing_errors.extend(lexing_errors.into_iter().map(Into::into)); - let parsed_module = module.unwrap_or_default(); - - (parsed_module, parsing_errors) -} - -/// program: module EOF -fn program() -> impl NoirParser { - module().then_ignore(just(Token::EOF)) -} - -/// module: top_level_statement module -/// | %empty -pub fn module() -> impl NoirParser { - recursive(|module_parser| { - inner_doc_comments() - .then( - empty() - .to(ParsedModule::default()) - .then(spanned(top_level_statement(module_parser)).repeated()) - .foldl(|mut program, (statement, span)| { - if let Some(kind) = statement.kind.into_item_kind() { - program.items.push(Item { - kind, - span, - doc_comments: statement.doc_comments, - }); - } - program - }), - ) - .map(|(doc_comments, mut program)| { - program.inner_doc_comments = doc_comments; - program - }) - }) -} - -/// This parser is used for parsing top level statements in macros -pub fn top_level_items() -> impl NoirParser> { - top_level_statement(module()).repeated() -} - -pub fn top_level_statement<'a>( - module_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - outer_doc_comments() - .then(top_level_statement_kind(module_parser)) - .map(|(doc_comments, kind)| TopLevelStatement { kind, doc_comments }) -} - -/// top_level_statement: function_definition -/// | struct_definition -/// | trait_definition -/// | implementation -/// | submodule -/// | module_declaration -/// | use_statement -/// | global_declaration -fn top_level_statement_kind<'a>( - module_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - choice(( - function::function_definition(false).map(TopLevelStatementKind::Function), - structs::struct_definition(), - traits::trait_definition(), - traits::trait_implementation(), - implementation(), - type_alias_definition().then_ignore(force(just(Token::Semicolon))), - submodule(module_parser.clone()), - contract(module_parser), - module_declaration().then_ignore(force(just(Token::Semicolon))), - use_statement().then_ignore(force(just(Token::Semicolon))), - global_declaration().then_ignore(force(just(Token::Semicolon))), - inner_attribute().map(TopLevelStatementKind::InnerAttribute), - )) - .recover_via(top_level_statement_recovery()) -} - -/// Parses a non-trait implementation, adding a set of methods to a type. -/// -/// implementation: 'impl' generics type '{' function_definition ... '}' -fn implementation() -> impl NoirParser { - let method = spanned(function::function_definition(true)); - let methods = outer_doc_comments() - .then(method) - .map(|(doc_comments, (method, span))| (Documented::new(method, doc_comments), span)) - .repeated(); - - let methods_or_error = just(Token::LeftBrace) - .ignore_then(methods) - .then_ignore(just(Token::RightBrace)) - .or_not() - .validate(|methods, span, emit| { - if let Some(methods) = methods { - methods - } else { - emit(ParserError::with_reason( - ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterImplType, - span, - )); - vec![] - } - }); - - keyword(Keyword::Impl) - .ignore_then(function::generics()) - .then(parse_type().map_with_span(|typ, span| (typ, span))) - .then(where_clause()) - .then(methods_or_error) - .map(|args| { - let ((other_args, where_clause), methods) = args; - let (generics, (object_type, type_span)) = other_args; - TopLevelStatementKind::Impl(TypeImpl { - generics, - object_type, - type_span, - where_clause, - methods, - }) - }) -} - -/// global_declaration: 'global' ident global_type_annotation '=' literal -fn global_declaration() -> impl NoirParser { - let p = attributes::attributes() - .then(item_visibility()) - .then(maybe_comp_time()) - .then(spanned(keyword(Keyword::Mut)).or_not()) - .then_ignore(keyword(Keyword::Global).labelled(ParsingRuleLabel::Global)) - .then(ident().map(Pattern::Identifier)); - - let p = then_commit(p, optional_type_annotation()); - let p = then_commit_ignore(p, just(Token::Assign)); - let p = then_commit(p, expression()); - p.validate( - |((((((attributes, visibility), comptime), mutable), mut pattern), r#type), expression), - span, - emit| { - let global_attributes = - attributes::validate_secondary_attributes(attributes, span, emit); - - // Only comptime globals are allowed to be mutable, but we always parse the `mut` - // and throw the error in name resolution. - if let Some((_, mut_span)) = mutable { - let span = mut_span.merge(pattern.span()); - pattern = Pattern::Mutable(Box::new(pattern), span, false); - } - ( - LetStatement { - pattern, - r#type, - comptime, - expression, - attributes: global_attributes, - }, - visibility, - ) - }, - ) - .map(|(let_statement, visibility)| TopLevelStatementKind::Global(let_statement, visibility)) -} - -/// submodule: 'mod' ident '{' module '}' -fn submodule( - module_parser: impl NoirParser, -) -> impl NoirParser { - attributes() - .then(item_visibility()) - .then_ignore(keyword(Keyword::Mod)) - .then(ident()) - .then_ignore(just(Token::LeftBrace)) - .then(module_parser) - .then_ignore(just(Token::RightBrace)) - .validate(|(((attributes, visibility), name), contents), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::SubModule(ParsedSubModule { - visibility, - name, - contents, - outer_attributes: attributes, - is_contract: false, - }) - }) -} - -/// contract: 'contract' ident '{' module '}' -fn contract( - module_parser: impl NoirParser, -) -> impl NoirParser { - attributes() - .then(item_visibility()) - .then_ignore(keyword(Keyword::Contract)) - .then(ident()) - .then_ignore(just(Token::LeftBrace)) - .then(module_parser) - .then_ignore(just(Token::RightBrace)) - .validate(|(((attributes, visibility), name), contents), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::SubModule(ParsedSubModule { - visibility, - name, - contents, - outer_attributes: attributes, - is_contract: true, - }) - }) -} - -fn type_alias_definition() -> impl NoirParser { - use self::Keyword::Type; - - item_visibility() - .then_ignore(keyword(Type)) - .then(ident()) - .then(function::generics()) - .then_ignore(just(Token::Assign)) - .then(parse_type()) - .map_with_span(|(((visibility, name), generics), typ), span| { - TopLevelStatementKind::TypeAlias(NoirTypeAlias { - name, - generics, - typ, - visibility, - span, - }) - }) -} - -fn self_parameter() -> impl NoirParser { - let mut_ref_pattern = just(Token::Ampersand).then_ignore(keyword(Keyword::Mut)); - let mut_pattern = keyword(Keyword::Mut); - - mut_ref_pattern - .or(mut_pattern) - .map_with_span(|token, span| (token, span)) - .or_not() - .then(filter_map(move |span, found: Token| match found { - Token::Ident(ref word) if word == "self" => Ok(span), - _ => Err(ParserError::expected_label(ParsingRuleLabel::Parameter, found, span)), - })) - .map(|(pattern_keyword, ident_span)| { - let ident = Ident::new("self".to_string(), ident_span); - let path = Path::from_single("Self".to_owned(), ident_span); - let no_args = GenericTypeArgs::default(); - let mut self_type = - UnresolvedTypeData::Named(path, no_args, true).with_span(ident_span); - let mut pattern = Pattern::Identifier(ident); - - match pattern_keyword { - Some((Token::Ampersand, _)) => { - self_type = UnresolvedTypeData::MutableReference(Box::new(self_type)) - .with_span(ident_span); - } - Some((Token::Keyword(_), span)) => { - pattern = Pattern::Mutable(Box::new(pattern), span.merge(ident_span), true); - } - _ => (), - } - - Param { span: pattern.span(), pattern, typ: self_type, visibility: Visibility::Private } - }) -} - -/// Function declaration parameters differ from other parameters in that parameter -/// patterns are not allowed in declarations. All parameters must be identifiers. -fn function_declaration_parameters() -> impl NoirParser> { - let typ = parse_type().recover_via(parameter_recovery()); - let typ = just(Token::Colon).ignore_then(typ); - - let full_parameter = ident().recover_via(parameter_name_recovery()).then(typ); - let self_parameter = self_parameter().validate(|param, span, emit| { - match param.pattern { - Pattern::Identifier(ident) => (ident, param.typ), - other => { - emit(ParserError::with_reason( - ParserErrorReason::PatternInTraitFunctionParameter, - span, - )); - // into_ident panics on tuple or struct patterns but should be fine to call here - // since the `self` parser can only parse `self`, `mut self` or `&mut self`. - (other.into_ident(), param.typ) + let lexer = Lexer::new(source_program); + let mut parser = Parser::for_lexer(lexer); + let program = parser.parse_program(); + let errors = parser.errors; + (program, errors) +} + +enum TokenStream<'a> { + Lexer(Lexer<'a>), + Tokens(Tokens), +} + +impl<'a> TokenStream<'a> { + fn next(&mut self) -> Option { + match self { + TokenStream::Lexer(lexer) => lexer.next(), + TokenStream::Tokens(tokens) => { + // NOTE: `TokenStream::Tokens` is only created via `Parser::for_tokens(tokens)` which + // reverses `tokens`. That's why using `pop` here is fine (done for performance reasons). + tokens.0.pop().map(Ok) } } - }); - - let parameter = full_parameter.or(self_parameter); - - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} - -fn block_expr<'a>( - statement: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - block(statement).map(ExpressionKind::Block).map_with_span(Expression::new) -} - -pub fn block<'a>( - statement: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - use Token::*; - - statement - .recover_via(statement_recovery()) - .then(just(Semicolon).or_not().map_with_span(|s, span| (s, span))) - .map_with_span(|(kind, rest), span| (Statement { kind, span }, rest)) - .repeated() - .validate(check_statements_require_semicolon) - .delimited_by(just(LeftBrace), just(RightBrace)) - .recover_with(nested_delimiters( - LeftBrace, - RightBrace, - [(LeftParen, RightParen), (LeftBracket, RightBracket)], - |span| vec![Statement { kind: StatementKind::Error, span }], - )) - .map(|statements| BlockExpression { statements }) + } } -fn check_statements_require_semicolon( - statements: Vec<(Statement, (Option, Span))>, - _span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Vec { - let last = statements.len().saturating_sub(1); - let iter = statements.into_iter().enumerate(); - vecmap(iter, |(i, (statement, (semicolon, span)))| { - statement.add_semicolon(semicolon, span, i == last, emit) - }) -} +pub struct Parser<'a> { + pub(crate) errors: Vec, + tokens: TokenStream<'a>, -/// Parse an optional ': type' -fn optional_type_annotation<'a>() -> impl NoirParser + 'a { - ignore_then_commit(just(Token::Colon), parse_type()).or_not().map_with_span(|r#type, span| { - r#type.unwrap_or(UnresolvedTypeData::Unspecified.with_span(span)) - }) + // We always have one look-ahead token for these cases: + // - check if we get `&` or `&mut` + // - check if we get `>` or `>>` + token: SpannedToken, + next_token: SpannedToken, + current_token_span: Span, + previous_token_span: Span, } -fn module_declaration() -> impl NoirParser { - attributes().then(item_visibility()).then_ignore(keyword(Keyword::Mod)).then(ident()).validate( - |((attributes, visibility), ident), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::Module(ModuleDeclaration { - visibility, - ident, - outer_attributes: attributes, - }) - }, - ) -} +impl<'a> Parser<'a> { + pub fn for_lexer(lexer: Lexer<'a>) -> Self { + Self::new(TokenStream::Lexer(lexer)) + } -fn use_statement() -> impl NoirParser { - item_visibility() - .then_ignore(keyword(Keyword::Use)) - .then(use_tree()) - .map(|(visibility, use_tree)| TopLevelStatementKind::Import(use_tree, visibility)) -} + pub fn for_tokens(mut tokens: Tokens) -> Self { + tokens.0.reverse(); + Self::new(TokenStream::Tokens(tokens)) + } -fn rename() -> impl NoirParser> { - ignore_then_commit(keyword(Keyword::As), ident()).or_not() -} + pub fn for_str(str: &'a str) -> Self { + Self::for_lexer(Lexer::new(str)) + } -fn use_tree() -> impl NoirParser { - recursive(|use_tree| { - let simple = path::path_no_turbofish().then(rename()).map(|(mut prefix, alias)| { - let ident = prefix.pop().ident; - UseTree { prefix, kind: UseTreeKind::Path(ident, alias) } - }); - - let list = { - let prefix = maybe_empty_path().then_ignore(just(Token::DoubleColon)); - let tree = use_tree - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) - .map(UseTreeKind::List); - - prefix.then(tree).map(|(prefix, kind)| UseTree { prefix, kind }) + fn new(tokens: TokenStream<'a>) -> Self { + let mut parser = Self { + errors: Vec::new(), + tokens, + token: eof_spanned_token(), + next_token: eof_spanned_token(), + current_token_span: Default::default(), + previous_token_span: Default::default(), }; + parser.read_two_first_tokens(); + parser + } - choice((list, simple)) - }) -} - -fn statement<'a, P, P2>( - expr_parser: P, - expr_no_constructors: P2, -) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - P2: ExprParser + 'a, -{ - recursive(|statement| { - choice(( - assertion::constrain(expr_parser.clone()), - assertion::assertion(expr_parser.clone()), - declaration(expr_parser.clone()), - assignment(expr_parser.clone()), - if_statement(expr_no_constructors.clone(), statement.clone()), - block_statement(statement.clone()), - for_loop(expr_no_constructors.clone(), statement.clone()), - break_statement(), - continue_statement(), - return_statement(expr_parser.clone()), - comptime_statement(expr_parser.clone(), expr_no_constructors, statement), - interned_statement(), - expr_parser.map(StatementKind::Expression), - )) - }) -} - -pub fn fresh_statement() -> impl NoirParser { - statement(expression(), expression_no_constructors(expression())) -} - -fn break_statement() -> impl NoirParser { - keyword(Keyword::Break).to(StatementKind::Break) -} - -fn continue_statement() -> impl NoirParser { - keyword(Keyword::Continue).to(StatementKind::Continue) -} - -fn comptime_statement<'a, P1, P2, S>( - expr: P1, - expr_no_constructors: P2, - statement: S, -) -> impl NoirParser + 'a -where - P1: ExprParser + 'a, - P2: ExprParser + 'a, - S: NoirParser + 'a, -{ - let comptime_statement = choice(( - declaration(expr), - for_loop(expr_no_constructors, statement.clone()), - block(statement).map_with_span(|block, span| { - StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) - }), - )) - .map_with_span(|kind, span| Box::new(Statement { kind, span })); - - keyword(Keyword::Comptime).ignore_then(comptime_statement).map(StatementKind::Comptime) -} - -/// Comptime in an expression position only accepts entire blocks -fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a -where - S: NoirParser + 'a, -{ - keyword(Keyword::Comptime) - .ignore_then(spanned(block(statement))) - .map(|(block, span)| ExpressionKind::Comptime(block, span)) -} + /// Program = Module + pub(crate) fn parse_program(&mut self) -> ParsedModule { + self.parse_module( + false, // nested + ) + } -fn unsafe_expr<'a, S>(statement: S) -> impl NoirParser + 'a -where - S: NoirParser + 'a, -{ - keyword(Keyword::Unsafe) - .ignore_then(spanned(block(statement))) - .map(|(block, span)| ExpressionKind::Unsafe(block, span)) -} + /// Module = InnerDocComments Item* + pub(crate) fn parse_module(&mut self, nested: bool) -> ParsedModule { + let inner_doc_comments = self.parse_inner_doc_comments(); + let items = self.parse_module_items(nested); -fn let_statement<'a, P>( - expr_parser: P, -) -> impl NoirParser<((Pattern, UnresolvedType), Expression)> + 'a -where - P: ExprParser + 'a, -{ - let p = - ignore_then_commit(keyword(Keyword::Let).labelled(ParsingRuleLabel::Statement), pattern()); - let p = p.then(optional_type_annotation()); - let p = then_commit_ignore(p, just(Token::Assign)); - then_commit(p, expr_parser) -} + ParsedModule { items, inner_doc_comments } + } -fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - attributes().then(let_statement(expr_parser)).validate( - |(attributes, ((pattern, typ), expr)), span, emit| { - let attributes = attributes::validate_secondary_attributes(attributes, span, emit); - StatementKind::new_let(pattern, typ, expr, attributes) - }, - ) -} + /// Parses an LValue. If an LValue can't be parsed, an error is recorded and a default LValue is returned. + pub(crate) fn parse_lvalue_or_error(&mut self) -> LValue { + let start_span = self.current_token_span; -pub fn pattern() -> impl NoirParser { - recursive(|pattern| { - let ident_pattern = ident().map(Pattern::Identifier).map_err(|mut error| { - if matches!(error.found(), Token::IntType(..)) { - error = ParserError::with_reason( - ParserErrorReason::ExpectedPatternButFoundType(error.found().clone()), - error.span(), - ); + if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { + match token.into_token() { + Token::InternedLValue(lvalue) => { + return LValue::Interned(lvalue, self.span_since(start_span)); + } + _ => unreachable!(), } + } - error - }); - - let mut_pattern = keyword(Keyword::Mut) - .ignore_then(pattern.clone()) - .map_with_span(|inner, span| Pattern::Mutable(Box::new(inner), span, false)); - - let short_field = ident().map(|name| (name.clone(), Pattern::Identifier(name))); - let long_field = ident().then_ignore(just(Token::Colon)).then(pattern.clone()); - - let struct_pattern_fields = long_field - .or(short_field) - .separated_by(just(Token::Comma)) - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); - - let struct_pattern = path(super::parse_type()) - .then(struct_pattern_fields) - .map_with_span(|(typename, fields), span| Pattern::Struct(typename, fields, span)); - - let tuple_pattern = pattern - .separated_by(just(Token::Comma)) - .delimited_by(just(Token::LeftParen), just(Token::RightParen)) - .map_with_span(Pattern::Tuple); - - let interned = - token_kind(TokenKind::InternedPattern).map_with_span(|token, span| match token { - Token::InternedPattern(id) => Pattern::Interned(id, span), - _ => unreachable!( - "token_kind(InternedPattern) guarantees we parse an interned pattern" - ), - }); - - choice((mut_pattern, tuple_pattern, struct_pattern, ident_pattern, interned)) - }) - .labelled(ParsingRuleLabel::Pattern) -} - -fn assignment<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - let fallible = - lvalue(expr_parser.clone()).then(assign_operator()).labelled(ParsingRuleLabel::Statement); - - then_commit(fallible, expr_parser).map_with_span( - |((identifier, operator), expression), span| { - StatementKind::assign(identifier, operator, expression, span) - }, - ) -} - -/// Parse an assignment operator `=` optionally prefixed by a binary operator for a combined -/// assign statement shorthand. Notably, this must handle a few corner cases with how `>>` is -/// lexed as two separate greater-than operators rather than a single right-shift. -fn assign_operator() -> impl NoirParser { - let shorthand_operators = Token::assign_shorthand_operators(); - // We need to explicitly check for right_shift here since it is actually - // two separate greater-than operators. - let shorthand_operators = right_shift_operator().or(one_of(shorthand_operators)); - let shorthand_syntax = shorthand_operators.then_ignore(just(Token::Assign)); - - // Since >> is lexed as two separate "greater-than"s, >>= is lexed as > >=, so - // we need to account for that case here as well. - let right_shift_fix = - just(Token::Greater).then(just(Token::GreaterEqual)).to(Token::ShiftRight); - - let shorthand_syntax = shorthand_syntax.or(right_shift_fix); - just(Token::Assign).or(shorthand_syntax) -} - -enum LValueRhs { - MemberAccess(Ident, Span), - Index(Expression, Span), -} - -pub fn lvalue<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - recursive(|lvalue| { - let l_ident = ident().map(LValue::Ident); - - let dereferences = just(Token::Star) - .ignore_then(lvalue.clone()) - .map_with_span(|lvalue, span| LValue::Dereference(Box::new(lvalue), span)); - - let parenthesized = lvalue.delimited_by(just(Token::LeftParen), just(Token::RightParen)); + let expr = self.parse_expression_or_error(); + if let Some(lvalue) = LValue::from_expression(expr) { + lvalue + } else { + self.expected_label(ParsingRuleLabel::LValue); + LValue::Ident(Ident::default()) + } + } - let interned = - token_kind(TokenKind::InternedLValue).map_with_span(|token, span| match token { - Token::InternedLValue(id) => LValue::Interned(id, span), - _ => unreachable!( - "token_kind(InternedLValue) guarantees we parse an interned lvalue" - ), - }); + /// Invokes `parsing_function` (`parsing_function` must be some `parse_*` method of the parser) + /// and returns the result if the parser has no errors, and if the parser consumed all tokens. + /// Otherwise returns the list of errors. + pub fn parse_result(mut self, parsing_function: F) -> Result> + where + F: FnOnce(&mut Parser<'a>) -> T, + { + let item = parsing_function(&mut self); + if !self.at_eof() { + self.expected_token(Token::EOF); + return Err(self.errors); + } - let term = choice((parenthesized, dereferences, l_ident, interned)); + if self.errors.is_empty() { + Ok(item) + } else { + Err(self.errors) + } + } - let l_member_rhs = - just(Token::Dot).ignore_then(field_name()).map_with_span(LValueRhs::MemberAccess); + /// Invokes `parsing_function` (`parsing_function` must be some `parse_*` method of the parser) + /// and returns the result if the parser has no errors, and if the parser consumed all tokens. + /// Otherwise returns None. + pub fn parse_option(self, parsing_function: F) -> Option + where + F: FnOnce(&mut Parser<'a>) -> Option, + { + match self.parse_result(parsing_function) { + Ok(item) => item, + Err(_) => None, + } + } - let l_index = expr_parser - .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .map_with_span(LValueRhs::Index); + /// Bumps this parser by one token. Returns the token that was previously the "current" token. + fn bump(&mut self) -> SpannedToken { + self.previous_token_span = self.current_token_span; + let next_next_token = self.read_token_internal(); + let next_token = std::mem::replace(&mut self.next_token, next_next_token); + let token = std::mem::replace(&mut self.token, next_token); + self.current_token_span = self.token.to_span(); + token + } - term.then(l_member_rhs.or(l_index).repeated()).foldl(|lvalue, rhs| match rhs { - LValueRhs::MemberAccess(field_name, span) => { - let span = lvalue.span().merge(span); - LValue::MemberAccess { object: Box::new(lvalue), field_name, span } - } - LValueRhs::Index(index, span) => { - let span = lvalue.span().merge(span); - LValue::Index { array: Box::new(lvalue), index, span } - } - }) - }) -} + fn read_two_first_tokens(&mut self) { + self.token = self.read_token_internal(); + self.current_token_span = self.token.to_span(); + self.next_token = self.read_token_internal(); + } -fn call_data() -> impl NoirParser { - keyword(Keyword::CallData).then(parenthesized(literal())).validate(|token, span, emit| { - match token { - (_, ExpressionKind::Literal(Literal::Integer(x, _))) => { - let id = x.to_u128() as u32; - Visibility::CallData(id) - } - _ => { - emit(ParserError::with_reason(ParserErrorReason::InvalidCallDataIdentifier, span)); - Visibility::CallData(0) + fn read_token_internal(&mut self) -> SpannedToken { + loop { + let token = self.tokens.next(); + if let Some(token) = token { + match token { + Ok(token) => return token, + Err(lexer_error) => self.errors.push(lexer_error.into()), + } + } else { + return eof_spanned_token(); } } - }) -} - -pub fn expression() -> impl ExprParser { - recursive(|expr| { - expression_with_precedence( - Precedence::Lowest, - expr.clone(), - expression_no_constructors(expr.clone()), - statement(expr.clone(), expression_no_constructors(expr)), - false, - true, - ) - }) - .labelled(ParsingRuleLabel::Expression) -} - -fn expression_no_constructors<'a, P>(expr_parser: P) -> impl ExprParser + 'a -where - P: ExprParser + 'a, -{ - recursive(|expr_no_constructors| { - expression_with_precedence( - Precedence::Lowest, - expr_parser.clone(), - expr_no_constructors.clone(), - statement(expr_parser, expr_no_constructors), - false, - false, - ) - }) - .labelled(ParsingRuleLabel::Expression) -} - -fn return_statement<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - ignore_then_commit(keyword(Keyword::Return), expr_parser.or_not()) - .validate(|_, span, emit| { - emit(ParserError::with_reason(ParserErrorReason::EarlyReturn, span)); - StatementKind::Error - }) - .labelled(ParsingRuleLabel::Statement) -} + } -// An expression is a single term followed by 0 or more (OP subexpression)* -// where OP is an operator at the given precedence level and subexpression -// is an expression at the current precedence level plus one. -fn expression_with_precedence<'a, P, P2, S>( - precedence: Precedence, - expr_parser: P, - expr_no_constructors: P2, - statement: S, - // True if we should only parse the restricted subset of operators valid within type expressions - is_type_expression: bool, - // True if we should also parse constructors `Foo { field1: value1, ... }` as an expression. - // This is disabled when parsing the condition of an if statement due to a parsing conflict - // with `then` bodies containing only a single variable. - allow_constructors: bool, -) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - P2: ExprParser + 'a, - S: NoirParser + 'a, -{ - if precedence == Precedence::Highest { - if is_type_expression { - type_expression_term(expr_parser).boxed().labelled(ParsingRuleLabel::Term) + fn eat_kind(&mut self, kind: TokenKind) -> Option { + if self.token.kind() == kind { + Some(self.bump()) } else { - term(expr_parser, expr_no_constructors, statement, allow_constructors) - .boxed() - .labelled(ParsingRuleLabel::Term) + None } - } else { - let next_precedence = - if is_type_expression { precedence.next_type_precedence() } else { precedence.next() }; - - let next_expr = expression_with_precedence( - next_precedence, - expr_parser, - expr_no_constructors, - statement, - is_type_expression, - allow_constructors, - ); - - next_expr - .clone() - .then(then_commit(operator_with_precedence(precedence), next_expr).repeated()) - .foldl(create_infix_expression) - .boxed() - .labelled(ParsingRuleLabel::Expression) } -} -fn create_infix_expression(lhs: Expression, (operator, rhs): (BinaryOp, Expression)) -> Expression { - let span = lhs.span.merge(rhs.span); - let infix = Box::new(InfixExpression { lhs, operator, rhs }); - - Expression { span, kind: ExpressionKind::Infix(infix) } -} - -fn operator_with_precedence(precedence: Precedence) -> impl NoirParser> { - right_shift_operator() - .or(any()) // Parse any single token, we're validating it as an operator next - .try_map(move |token, span| { - if Precedence::token_precedence(&token) == Some(precedence) { - Ok(token.try_into_binary_op(span).unwrap()) + fn eat_keyword(&mut self, keyword: Keyword) -> bool { + if let Token::Keyword(kw) = self.token.token() { + if *kw == keyword { + self.bump(); + true } else { - Err(ParserError::expected_label(ParsingRuleLabel::BinaryOperator, token, span)) + false } - }) -} - -fn term<'a, P, P2, S>( - expr_parser: P, - expr_no_constructors: P2, - statement: S, - allow_constructors: bool, -) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - P2: ExprParser + 'a, - S: NoirParser + 'a, -{ - recursive(move |term_parser| { - choice(( - not(term_parser.clone()), - negation(term_parser.clone()), - mutable_reference(term_parser.clone()), - dereference(term_parser), - )) - .map_with_span(Expression::new) - // right-unary operators like a[0] or a.f bind more tightly than left-unary - // operators like - or !, so that !a[0] is parsed as !(a[0]). This is a bit - // awkward for casts so -a as i32 actually binds as -(a as i32). - .or(atom_or_right_unary( - expr_parser, - expr_no_constructors, - statement, - allow_constructors, - parse_type(), - )) - }) -} - -/// The equivalent of a 'term' for use in type expressions. Unlike regular terms, the grammar here -/// is restricted to no longer include right-unary expressions, unary not, and most atoms. -fn type_expression_term<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - recursive(move |term_parser| { - negation(term_parser).map_with_span(Expression::new).or(type_expression_atom(expr_parser)) - }) -} - -fn atom_or_right_unary<'a, P, P2, S>( - expr_parser: P, - expr_no_constructors: P2, - statement: S, - allow_constructors: bool, - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - P2: ExprParser + 'a, - S: NoirParser + 'a, -{ - enum UnaryRhs { - Call((Option, Vec)), - ArrayIndex(Expression), - Cast(UnresolvedType), - MemberAccess(UnaryRhsMemberAccess), - /// This is to allow `foo.` (no identifier afterwards) to be parsed as `foo` - /// and produce an error, rather than just erroring (for LSP). - JustADot, + } else { + false + } } - // `(arg1, ..., argN)` in `my_func(arg1, ..., argN)` - // 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 - .clone() - .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .map(UnaryRhs::ArrayIndex); - - // `as Type` in `atom as Type` - let cast_rhs = keyword(Keyword::As) - .ignore_then(type_parser.clone()) - .map(UnaryRhs::Cast) - .labelled(ParsingRuleLabel::Cast); - - // 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()))) - .validate(|((turbofish, macro_call), args), span, emit| { - if turbofish.as_ref().map_or(false, |generics| !generics.named_args.is_empty()) { - let reason = ParserErrorReason::AssociatedTypesNotAllowedInMethodCalls; - emit(ParserError::with_reason(reason, span)); + fn eat_ident(&mut self) -> Option { + if let Some(token) = self.eat_kind(TokenKind::Ident) { + match token.into_token() { + Token::Ident(ident) => Some(Ident::new(ident, self.previous_token_span)), + _ => unreachable!(), } + } else { + None + } + } - let macro_call = macro_call.is_some(); - let turbofish = turbofish.map(|generics| generics.ordered_args); - UnaryRhsMethodCall { turbofish, macro_call, args } - }); - - // `.foo` or `.foo(args)` in `atom.foo` or `atom.foo(args)` - let member_rhs = just(Token::Dot) - .ignore_then(field_name()) - .then(method_call_rhs.or_not()) - .map(|(method_or_field, method_call)| { - UnaryRhs::MemberAccess(UnaryRhsMemberAccess { method_or_field, method_call }) - }) - .labelled(ParsingRuleLabel::FieldAccess); - - let just_a_dot = - just(Token::Dot).map(|_| UnaryRhs::JustADot).validate(|value, span, emit_error| { - emit_error(ParserError::with_reason( - ParserErrorReason::ExpectedIdentifierAfterDot, - span, - )); - value - }); - - let rhs = choice((call_rhs, array_rhs, cast_rhs, member_rhs, just_a_dot)); - - foldl_with_span( - atom(expr_parser, expr_no_constructors, statement, allow_constructors), - rhs, - |lhs, rhs, span| match rhs { - 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) => { - Expression::member_access_or_method_call(lhs, field, span) + fn eat_self(&mut self) -> bool { + if let Token::Ident(ident) = self.token.token() { + if ident == "self" { + self.bump(); + return true; } - UnaryRhs::JustADot => lhs, - }, - ) -} - -fn if_expr<'a, P, S>(expr_no_constructors: P, statement: S) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - S: NoirParser + 'a, -{ - recursive(|if_parser| { - let if_block = block_expr(statement.clone()); - // The else block could also be an `else if` block, in which case we must recursively parse it. - let else_block = block_expr(statement).or(if_parser.map_with_span(|kind, span| { - // Wrap the inner `if` expression in a block expression. - // i.e. rewrite the sugared form `if cond1 {} else if cond2 {}` as `if cond1 {} else { if cond2 {} }`. - let if_expression = Expression::new(kind, span); - let desugared_else = BlockExpression { - statements: vec![Statement { - kind: StatementKind::Expression(if_expression), - span, - }], - }; - Expression::new(ExpressionKind::Block(desugared_else), span) - })); - - keyword(Keyword::If) - .ignore_then(expr_no_constructors) - .then(if_block.then(keyword(Keyword::Else).ignore_then(else_block).or_not()).or_not()) - .validate(|(condition, consequence_and_alternative), span, emit_error| { - if let Some((consequence, alternative)) = consequence_and_alternative { - ExpressionKind::If(Box::new(IfExpression { - condition, - consequence, - alternative, - })) - } else { - // We allow `if cond` without a block mainly for LSP, so that it parses right - // and autocompletion works there. - emit_error(ParserError::with_reason( - ParserErrorReason::ExpectedLeftBraceAfterIfCondition, - span, - )); - - let span_end = condition.span.end(); - ExpressionKind::If(Box::new(IfExpression { - condition, - consequence: Expression::new( - ExpressionKind::Error, - Span::from(span_end..span_end), - ), - alternative: None, - })) - } - }) - }) -} - -fn if_statement<'a, P, S>( - expr_no_constructors: P, - statement: S, -) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - S: NoirParser + 'a, -{ - if_expr(expr_no_constructors, statement).map_with_span(|expression_kind, span| { - StatementKind::Expression(Expression::new(expression_kind, span)) - }) -} - -fn block_statement<'a, S>(statement: S) -> impl NoirParser + 'a -where - S: NoirParser + 'a, -{ - block(statement).map_with_span(|block, span| { - StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) - }) -} + } -fn for_loop<'a, P, S>(expr_no_constructors: P, statement: S) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - S: NoirParser + 'a, -{ - keyword(Keyword::For) - .ignore_then(ident()) - .then_ignore(keyword(Keyword::In)) - .then(for_range(expr_no_constructors)) - .then(block_expr(statement)) - .map_with_span(|((identifier, range), block), span| { - StatementKind::For(ForLoopStatement { identifier, range, block, span }) - }) -} + false + } -/// The 'range' of a for loop. Either an actual range `start .. end` or an array expression. -fn for_range

(expr_no_constructors: P) -> impl NoirParser -where - P: ExprParser, -{ - expr_no_constructors - .clone() - .then(just(Token::DoubleDot).or(just(Token::DoubleDotEqual))) - .then(expr_no_constructors.clone()) - .map(|((start, dots), end)| { - if dots == Token::DoubleDotEqual { - ForRange::range_inclusive(start, end) - } else { - ForRange::range(start, end) + fn eat_int_type(&mut self) -> Option { + let is_int_type = matches!(self.token.token(), Token::IntType(..)); + if is_int_type { + let token = self.bump(); + match token.into_token() { + Token::IntType(int_type) => Some(int_type), + _ => unreachable!(), } - }) - .or(expr_no_constructors.map(ForRange::Array)) -} - -fn array_expr

(expr_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - standard_array(expr_parser.clone()).or(array_sugar(expr_parser)) -} - -/// [a, b, c, ...] -fn standard_array

(expr_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - expression_list(expr_parser) - .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .validate(|elements, _span, _emit| ExpressionKind::array(elements)) -} - -/// [a; N] -fn array_sugar

(expr_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - expr_parser - .clone() - .then(just(Token::Semicolon).ignore_then(expr_parser)) - .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .map(|(lhs, count)| ExpressionKind::repeated_array(lhs, count)) -} - -fn slice_expr

(expr_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Ampersand) - .ignore_then(standard_slice(expr_parser.clone()).or(slice_sugar(expr_parser))) -} - -/// &[a, b, c, ...] -fn standard_slice

(expr_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - expression_list(expr_parser) - .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .validate(|elements, _span, _emit| ExpressionKind::slice(elements)) -} - -/// &[a; N] -fn slice_sugar

(expr_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - expr_parser - .clone() - .then(just(Token::Semicolon).ignore_then(expr_parser)) - .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .map(|(lhs, count)| ExpressionKind::repeated_slice(lhs, count)) -} - -fn expression_list

(expr_parser: P) -> impl NoirParser> -where - P: ExprParser, -{ - expr_parser.separated_by(just(Token::Comma)).allow_trailing() -} - -/// Atoms are parameterized on whether constructor expressions are allowed or not. -/// Certain constructs like `if` and `for` disallow constructor expressions when a -/// block may be expected. -fn atom<'a, P, P2, S>( - expr_parser: P, - expr_no_constructors: P2, - statement: S, - allow_constructors: bool, -) -> impl NoirParser + 'a -where - P: ExprParser + 'a, - P2: ExprParser + 'a, - S: NoirParser + 'a, -{ - choice(( - if_expr(expr_no_constructors, statement.clone()), - slice_expr(expr_parser.clone()), - array_expr(expr_parser.clone()), - if allow_constructors { - constructor(expr_parser.clone()).boxed() } else { - nothing().boxed() - }, - lambdas::lambda(expr_parser.clone()), - block(statement.clone()).map(ExpressionKind::Block), - comptime_expr(statement.clone()), - unsafe_expr(statement), - quote(), - unquote(expr_parser.clone()), - variable(), - literal(), - as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), - type_path(parse_type()), - macro_quote_marker(), - interned_expr(), - interned_statement_expr(), - )) - .map_with_span(Expression::new) - .or(parenthesized(expr_parser.clone()).map_with_span(|sub_expr, span| { - Expression::new(ExpressionKind::Parenthesized(sub_expr.into()), span) - })) - .or(tuple(expr_parser)) - .labelled(ParsingRuleLabel::Atom) -} - -/// Atoms within type expressions are limited to only variables, literals, and parenthesized -/// type expressions. -fn type_expression_atom<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - primitives::variable_no_turbofish() - .or(literal()) - .map_with_span(Expression::new) - .or(parenthesized(expr_parser)) - .labelled(ParsingRuleLabel::Atom) -} - -fn quote() -> impl NoirParser { - token_kind(TokenKind::Quote).map(|token| { - ExpressionKind::Quote(match token { - Token::Quote(tokens) => tokens, - _ => unreachable!("token_kind(Quote) should guarantee parsing only a quote token"), - }) - }) -} - -/// 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)); - just(Token::DollarSign).ignore_then(unquote).map(|expr| ExpressionKind::Unquote(Box::new(expr))) -} + None + } + } -fn tuple

(expr_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - parenthesized(expression_list(expr_parser)).map_with_span(|elements, span| { - let kind = if elements.is_empty() { - ExpressionKind::Literal(Literal::Unit) + fn eat_int(&mut self) -> Option { + if matches!(self.token.token(), Token::Int(..)) { + let token = self.bump(); + match token.into_token() { + Token::Int(int) => Some(int), + _ => unreachable!(), + } } else { - ExpressionKind::Tuple(elements) - }; - Expression::new(kind, span) - }) -} - -fn field_name() -> impl NoirParser { - ident().or(token_kind(TokenKind::Literal).validate(|token, span, emit| match token { - Token::Int(_) => Ident::from(Spanned::from(span, token.to_string())), - other => { - emit(ParserError::with_reason(ParserErrorReason::ExpectedFieldName(other), span)); - Ident::error(span) + None } - })) -} - -fn constructor(expr_parser: impl ExprParser) -> impl NoirParser { - let args = constructor_field(expr_parser) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); - - let path = path(super::parse_type()).map(UnresolvedType::from_path); - let interned_unresolved_type = interned_unresolved_type(); - let typ = choice((path, interned_unresolved_type)); - - typ.then(args).map(ExpressionKind::constructor) -} - -fn constructor_field

(expr_parser: P) -> impl NoirParser<(Ident, Expression)> -where - P: ExprParser, -{ - let long_form = ident().then_ignore(just(Token::Colon)).then(expr_parser); - let short_form = ident().map(|ident| (ident.clone(), ident.into())); - long_form.or(short_form) -} - -#[cfg(test)] -mod test { - use super::test_helpers::*; - use super::*; - use crate::ast::ArrayLiteral; - - #[test] - fn parse_infix() { - let valid = vec!["x + 6", "x - k", "x + (x + a)", " x * (x + a) + (x - 4)"]; - parse_all(expression(), valid); - parse_all_failing(expression(), vec!["y ! x"]); } - #[test] - fn parse_function_call() { - let valid = vec![ - "std::hash ()", - " std::hash(x,y,a+b)", - "crate::foo (x)", - "hash (x,)", - "(foo + bar)()", - "(bar)()()()", - ]; - parse_all(expression(), valid); + fn eat_bool(&mut self) -> Option { + if matches!(self.token.token(), Token::Bool(..)) { + let token = self.bump(); + match token.into_token() { + Token::Bool(bool) => Some(bool), + _ => unreachable!(), + } + } else { + None + } } - #[test] - fn parse_cast() { - let expression_nc = expression_no_constructors(expression()); - parse_all( - atom_or_right_unary( - expression(), - expression_no_constructors(expression()), - fresh_statement(), - true, - parse_type(), - ), - vec!["x as u8", "x as u16", "0 as Field", "(x + 3) as [Field; 8]"], - ); - parse_all_failing( - atom_or_right_unary(expression(), expression_nc, fresh_statement(), true, parse_type()), - vec!["x as pub u8"], - ); + fn eat_str(&mut self) -> Option { + if matches!(self.token.token(), Token::Str(..)) { + let token = self.bump(); + match token.into_token() { + Token::Str(string) => Some(string), + _ => unreachable!(), + } + } else { + None + } } - #[test] - fn parse_array_index() { - let valid = vec![ - "x[9]", - "y[x+a]", - " foo [foo+5]", - "baz[bar]", - "foo.bar[3] as Field .baz as u32 [7]", - ]; - parse_all( - atom_or_right_unary( - expression(), - expression_no_constructors(expression()), - fresh_statement(), - true, - parse_type(), - ), - valid, - ); + fn eat_raw_str(&mut self) -> Option<(String, u8)> { + if matches!(self.token.token(), Token::RawStr(..)) { + let token = self.bump(); + match token.into_token() { + Token::RawStr(string, n) => Some((string, n)), + _ => unreachable!(), + } + } else { + None + } } - fn expr_to_array(expr: ExpressionKind) -> ArrayLiteral { - let lit = match expr { - ExpressionKind::Literal(literal) => literal, - _ => unreachable!("expected a literal"), - }; - - match lit { - Literal::Array(arr) => arr, - _ => unreachable!("expected an array"), + fn eat_fmt_str(&mut self) -> Option { + if matches!(self.token.token(), Token::FmtStr(..)) { + let token = self.bump(); + match token.into_token() { + Token::FmtStr(string) => Some(string), + _ => unreachable!(), + } + } else { + None } } - #[test] - fn parse_array() { - let valid = vec![ - "[0, 1, 2,3, 4]", - "[0,1,2,3,4,]", // Trailing commas are valid syntax - "[0;5]", - ]; - - for expr in parse_all(array_expr(expression()), valid) { - match expr_to_array(expr) { - ArrayLiteral::Standard(elements) => assert_eq!(elements.len(), 5), - ArrayLiteral::Repeated { length, .. } => { - assert_eq!(length.kind, ExpressionKind::integer(5i128.into())); - } + fn eat_quote(&mut self) -> Option { + if matches!(self.token.token(), Token::Quote(..)) { + let token = self.bump(); + match token.into_token() { + Token::Quote(tokens) => Some(tokens), + _ => unreachable!(), } + } else { + None } - - parse_all_failing( - array_expr(expression()), - vec!["0,1,2,3,4]", "[[0,1,2,3,4]", "[0,1,2,,]", "[0,1,2,3,4"], - ); } - #[test] - fn parse_array_sugar() { - let valid = vec!["[0;7]", "[(1, 2); 4]", "[0;Four]", "[2;1+3-a]"]; - parse_all(array_expr(expression()), valid); - - let invalid = vec!["[0;;4]", "[1, 2; 3]"]; - parse_all_failing(array_expr(expression()), invalid); + fn eat_comma(&mut self) -> bool { + self.eat(Token::Comma) } - fn expr_to_slice(expr: ExpressionKind) -> ArrayLiteral { - let lit = match expr { - ExpressionKind::Literal(literal) => literal, - _ => unreachable!("expected a literal"), - }; - - match lit { - Literal::Slice(arr) => arr, - _ => unreachable!("expected a slice: {:?}", lit), + fn eat_commas(&mut self) -> bool { + if self.eat_comma() { + while self.eat_comma() { + self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_span); + } + true + } else { + false } } - #[test] - fn parse_slice() { - let valid = vec![ - "&[0, 1, 2,3, 4]", - "&[0,1,2,3,4,]", // Trailing commas are valid syntax - "&[0;5]", - ]; - - for expr in parse_all(slice_expr(expression()), valid) { - match expr_to_slice(expr) { - ArrayLiteral::Standard(elements) => assert_eq!(elements.len(), 5), - ArrayLiteral::Repeated { length, .. } => { - assert_eq!(length.kind, ExpressionKind::integer(5i128.into())); - } + fn eat_semicolon(&mut self) -> bool { + self.eat(Token::Semicolon) + } + + fn eat_semicolons(&mut self) -> bool { + if self.eat_semicolon() { + while self.eat_semicolon() { + self.push_error(ParserErrorReason::UnexpectedSemicolon, self.previous_token_span); } + true + } else { + false } - - parse_all_failing( - slice_expr(expression()), - vec!["0,1,2,3,4]", "&[[0,1,2,3,4]", "&[0,1,2,,]", "&[0,1,2,3,4"], - ); } - #[test] - fn parse_slice_sugar() { - let valid = vec!["&[0;7]", "&[(1, 2); 4]", "&[0;Four]", "&[2;1+3-a]"]; - parse_all(slice_expr(expression()), valid); - - let invalid = vec!["&[0;;4]", "&[1, 2; 3]"]; - parse_all_failing(slice_expr(expression()), invalid); + fn eat_colon(&mut self) -> bool { + self.eat(Token::Colon) } - #[test] - fn parse_block() { - parse_with(block(fresh_statement()), "{ [0,1,2,3,4] }").unwrap(); - - parse_all_failing( - block(fresh_statement()), - vec![ - "[0,1,2,3,4] }", - "{ [0,1,2,3,4]", - "{ [0,1,2,,] }", // Contents of the block must still be a valid expression - "{ [0,1,2,3 }", - "{ 0,1,2,3] }", - "[[0,1,2,3,4]}", - ], - ); + fn eat_double_colon(&mut self) -> bool { + self.eat(Token::DoubleColon) } - #[test] - fn parse_let() { - // Why is it valid to specify a let declaration as having type u8? - // - // Let statements are not type checked here, so the parser will accept as - // long as it is a type. Other statements such as Public are type checked - // Because for now, they can only have one type - parse_all( - declaration(expression()), - vec!["let _ = 42", "let x = y", "let x : u8 = y", "let x: u16 = y"], - ); + fn eat_left_paren(&mut self) -> bool { + self.eat(Token::LeftParen) } - #[test] - fn parse_invalid_pub() { - // pub cannot be used to declare a statement - parse_all_failing(fresh_statement(), vec!["pub x = y", "pub x : pub Field = y"]); + fn eat_right_paren(&mut self) -> bool { + self.eat(Token::RightParen) } - #[test] - fn parse_for_loop() { - parse_all( - for_loop(expression_no_constructors(expression()), fresh_statement()), - vec![ - "for i in x+y..z {}", - "for i in 0..100 { foo; bar }", - "for i in 90..=100 { foo; bar }", - ], - ); - - parse_all_failing( - for_loop(expression_no_constructors(expression()), fresh_statement()), - vec![ - "for 1 in x+y..z {}", // Cannot have a literal as the loop identifier - "for i in 0...100 {}", // Only '..' is supported - "for i in .. {}", // Range needs needs bounds - "for i in ..=10 {}", // Range needs lower bound - "for i in 0.. {}", // Range needs upper bound - ], - ); + fn eat_left_brace(&mut self) -> bool { + self.eat(Token::LeftBrace) } - #[test] - fn parse_parenthesized_expression() { - parse_all( - atom(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["(0)", "(x+a)", "({(({{({(nested)})}}))})"], - ); - parse_all_failing( - atom(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["(x+a", "((x+a)", "(,)"], - ); + fn eat_left_bracket(&mut self) -> bool { + self.eat(Token::LeftBracket) } - #[test] - fn parse_tuple() { - parse_all(tuple(expression()), vec!["()", "(x,)", "(a,b+2)", "(a,(b,c,),d,)"]); + fn eat_right_bracket(&mut self) -> bool { + self.eat(Token::RightBracket) } - #[test] - fn parse_if_expr() { - parse_all( - if_expr(expression_no_constructors(expression()), fresh_statement()), - vec!["if x + a { } else { }", "if x {}", "if x {} else if y {} else {}"], - ); - - parse_all_failing( - if_expr(expression_no_constructors(expression()), fresh_statement()), - vec!["if (x / a) + 1 {} else", "if foo then 1 else 2", "if true { 1 }else 3"], - ); + fn eat_less(&mut self) -> bool { + self.eat(Token::Less) } - #[test] - fn parse_if_without_block() { - let src = "if foo"; - let parser = if_expr(expression_no_constructors(expression()), fresh_statement()); - let (expression_kind, errors) = parse_recover(parser, src); - - let expression_kind = expression_kind.unwrap(); - let ExpressionKind::If(if_expression) = expression_kind else { - panic!("Expected an if expression, got {:?}", expression_kind); - }; - - assert_eq!(if_expression.consequence.kind, ExpressionKind::Error); - assert_eq!(if_expression.alternative, None); - - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected { after if condition"); + fn eat_assign(&mut self) -> bool { + self.eat(Token::Assign) } - #[test] - fn parse_module_declaration() { - parse_with(module_declaration(), "mod foo").unwrap(); - parse_with(module_declaration(), "#[attr] mod foo").unwrap(); - parse_with(module_declaration(), "mod 1").unwrap_err(); + fn eat_dot(&mut self) -> bool { + self.eat(Token::Dot) } - #[test] - fn parse_submodule_declaration() { - parse_with(submodule(module()), "mod foo {}").unwrap(); - parse_with(submodule(module()), "#[attr] mod foo {}").unwrap(); + fn eat_pipe(&mut self) -> bool { + self.eat(Token::Pipe) } - #[test] - fn parse_contract() { - parse_with(contract(module()), "contract foo {}").unwrap(); - parse_with(contract(module()), "#[attr] contract foo {}").unwrap(); + fn eat(&mut self, token: Token) -> bool { + if self.token.token() == &token { + self.bump(); + true + } else { + false + } } - #[test] - fn parse_use() { - let valid_use_statements = [ - "use std::hash", - "use std", - "use foo::bar as hello", - "use bar as bar", - "use foo::{}", - "use foo::{bar,}", - "use foo::{bar, hello}", - "use foo::{bar as bar2, hello}", - "use foo::{bar as bar2, hello::{foo}, nested::{foo, bar}}", - "use std::{println, bar::baz}", - ]; - - let invalid_use_statements = [ - "use std as ;", - "use foobar as as;", - "use hello:: as foo;", - "use foo bar::baz", - "use foo bar::{baz}", - "use foo::{,}", - ]; - - let use_statements = valid_use_statements - .into_iter() - .map(|valid_str| (valid_str, true)) - .chain(invalid_use_statements.into_iter().map(|invalid_str| (invalid_str, false))); - - for (use_statement_str, expect_valid) in use_statements { - let mut use_statement_str = use_statement_str.to_string(); - if expect_valid { - let (result_opt, _diagnostics) = - parse_recover(&use_statement(), &use_statement_str); - use_statement_str.push(';'); - match result_opt.unwrap() { - TopLevelStatementKind::Import(expected_use_statement, _visibility) => { - Some(expected_use_statement) - } - _ => unreachable!(), - } - } else { - let result = parse_with(&use_statement(), &use_statement_str); - assert!(result.is_err()); - None - }; + fn eat_keyword_or_error(&mut self, keyword: Keyword) { + if !self.eat_keyword(keyword) { + self.expected_token(Token::Keyword(keyword)); } } - #[test] - fn parse_type_aliases() { - let cases = vec!["type foo = u8", "type bar = String", "type baz = Vec"]; - parse_all(type_alias_definition(), cases); - - let failing = vec!["type = u8", "type foo", "type foo = 1"]; - parse_all_failing(type_alias_definition(), failing); + fn eat_or_error(&mut self, token: Token) { + if !self.eat(token.clone()) { + self.expected_token(token); + } } - #[test] - fn parse_member_access() { - let cases = vec!["a.b", "a + b.c", "foo.bar as u32"]; - parse_all(expression(), cases); + fn at(&self, token: Token) -> bool { + self.token.token() == &token } - #[test] - fn parse_constructor() { - let cases = vec![ - "Baz", - "Bar { ident: 32 }", - "Baz { other: 2 + 42, ident: foo() + 1 }", - "Baz { other, ident: foo() + 1, foo }", - ]; - - parse_all(expression(), cases); - parse_with(expression(), "Foo { a + b }").unwrap_err(); + fn at_keyword(&self, keyword: Keyword) -> bool { + self.at(Token::Keyword(keyword)) } - // Semicolons are: - // - Required after non-expression statements - // - Optional after for, if, block expressions - // - Optional after an expression as the last statement of a block - // - Required after an expression as the non-final statement of a block - #[test] - fn parse_semicolons() { - let cases = vec![ - "{ if true {} if false {} foo }", - "{ if true {}; if false {} foo }", - "{ for x in 0..1 {} if false {} foo; }", - "{ let x = 2; }", - "{ expr1; expr2 }", - "{ expr1; expr2; }", - ]; - parse_all(block(fresh_statement()), cases); - - let failing = vec![ - // We disallow multiple semicolons after a statement unlike rust where it is a warning - "{ test;; foo }", - "{ for x in 0..1 {} foo if false {} }", - "{ let x = 2 }", - "{ expr1 expr2 }", - ]; - parse_all_failing(block(fresh_statement()), failing); + fn next_is(&self, token: Token) -> bool { + self.next_token.token() == &token } - #[test] - fn statement_recovery() { - let cases = vec![ - Case { source: "let a = 4 + 3", expect: "let a = (4 + 3)", errors: 0 }, - Case { source: "let a: = 4 + 3", expect: "let a: error = (4 + 3)", errors: 1 }, - Case { source: "let = 4 + 3", expect: "let $error = (4 + 3)", errors: 1 }, - Case { source: "let = ", expect: "let $error = Error", errors: 2 }, - Case { source: "let", expect: "let $error = Error", errors: 3 }, - Case { source: "foo = one two three", expect: "foo = one", errors: 1 }, - Case { source: "constrain", expect: "constrain Error", errors: 2 }, - Case { source: "assert", expect: "assert()", errors: 1 }, - Case { source: "constrain x ==", expect: "constrain (x == Error)", errors: 2 }, - Case { source: "assert(x ==)", expect: "assert((x == Error))", errors: 1 }, - Case { source: "assert(x == x, x)", expect: "assert((x == x), x)", errors: 0 }, - Case { source: "assert_eq(x,)", expect: "assert_eq(x)", errors: 0 }, - Case { source: "assert_eq(x, x, x, x)", expect: "assert_eq(x, x, x, x)", errors: 0 }, - Case { source: "assert_eq(x, x, x)", expect: "assert_eq(x, x, x)", errors: 0 }, - ]; - - check_cases_with_errors(&cases[..], fresh_statement()); + fn at_eof(&self) -> bool { + self.token.token() == &Token::EOF } - #[test] - fn return_validation() { - let cases = [ - Case { - source: "{ return 42; }", - expect: concat!("{\n", " Error\n", "}",), - errors: 1, - }, - Case { - source: "{ return 1; return 2; }", - expect: concat!("{\n", " Error\n", " Error\n", "}"), - errors: 2, - }, - Case { - source: "{ return 123; let foo = 4 + 3; }", - expect: concat!("{\n", " Error\n", " let foo = (4 + 3)\n", "}"), - errors: 1, - }, - Case { - source: "{ return 1 + 2 }", - expect: concat!("{\n", " Error\n", "}",), - errors: 2, - }, - Case { source: "{ return; }", expect: concat!("{\n", " Error\n", "}",), errors: 1 }, - ]; - - check_cases_with_errors(&cases[..], block(fresh_statement())); + fn span_since(&self, start_span: Span) -> Span { + if self.current_token_span == start_span { + start_span + } else { + let end_span = self.previous_token_span; + Span::from(start_span.start()..end_span.end()) + } } - #[test] - fn expr_no_constructors() { - let cases = [ - Case { - source: "{ if structure { a: 1 } {} }", - expect: concat!( - "{\n", - " if structure {\n", - " Error\n", - " }\n", - " {\n", - " }\n", - "}", - ), - errors: 1, - }, - Case { - source: "{ if ( structure { a: 1 } ) {} }", - expect: concat!("{\n", " if ((structure { a: 1 })) {\n", " }\n", "}",), - errors: 0, - }, - Case { - source: "{ if ( structure {} ) {} }", - expect: concat!("{\n", " if ((structure { })) {\n", " }\n", "}"), - errors: 0, - }, - Case { - source: "{ if (a { x: 1 }, b { y: 2 }) {} }", - expect: concat!("{\n", " if ((a { x: 1 }), (b { y: 2 })) {\n", " }\n", "}",), - errors: 0, - }, - Case { - source: "{ if ({ let foo = bar { baz: 42 }; foo == bar { baz: 42 }}) {} }", - expect: concat!( - "{\n", - " if ({\n", - " let foo = (bar { baz: 42 })\n", - " (foo == (bar { baz: 42 }))\n", - " }) {\n", - " }\n", - "}", - ), - errors: 0, - }, - ]; - - check_cases_with_errors(&cases[..], block(fresh_statement())); + fn span_at_previous_token_end(&self) -> Span { + Span::from(self.previous_token_span.end()..self.previous_token_span.end()) } - #[test] - fn test_quote() { - let cases = vec![ - "quote {}", - "quote { a.b }", - "quote { ) ( }", // invalid syntax is fine in a quote - "quote { { } }", // Nested `{` and `}` shouldn't close the quote as long as they are matched. - "quote { 1 { 2 { 3 { 4 { 5 } 4 4 } 3 3 } 2 2 } 1 1 }", - ]; - parse_all(quote(), cases); - - let failing = vec!["quote {}}", "quote a", "quote { { { } } } }"]; - parse_all_failing(quote(), failing); + fn expected_identifier(&mut self) { + self.expected_label(ParsingRuleLabel::Identifier); } - #[test] - fn test_parses_block_statement_not_infix_expression() { - let src = r#" - { - {} - -1 - }"#; - let (block_expr, _) = parse_recover(block(fresh_statement()), src); - let block_expr = block_expr.expect("Failed to parse module"); - assert_eq!(block_expr.statements.len(), 2); + fn expected_token(&mut self, token: Token) { + self.errors.push(ParserError::expected_token( + token, + self.token.token().clone(), + self.current_token_span, + )); } - #[test] - fn test_parses_if_statement_not_infix_expression() { - let src = r#" - { - if 1 { 2 } else { 3 } - -1 - }"#; - let (block_expr, _) = parse_recover(block(fresh_statement()), src); - let block_expr = block_expr.expect("Failed to parse module"); - assert_eq!(block_expr.statements.len(), 2); + fn expected_one_of_tokens(&mut self, tokens: &[Token]) { + self.errors.push(ParserError::expected_one_of_tokens( + tokens, + self.token.token().clone(), + self.current_token_span, + )); } - #[test] - fn test_parses_if_statement_followed_by_tuple_as_two_separate_statements() { - // Regression for #1310: this should not be parsed as a function call - let src = r#" - { - if 1 { 2 } else { 3 } (1, 2) - }"#; - let (block_expr, _) = parse_recover(block(fresh_statement()), src); - let block_expr = block_expr.expect("Failed to parse module"); - assert_eq!(block_expr.statements.len(), 2); + fn expected_label(&mut self, label: ParsingRuleLabel) { + self.errors.push(ParserError::expected_label( + label, + self.token.token().clone(), + self.current_token_span, + )); } - #[test] - fn test_parses_member_access_without_member_name() { - let src = "{ foo. }"; + fn expected_token_separating_items(&mut self, token: Token, items: &'static str, span: Span) { + self.push_error(ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, span); + } - let (Some(block_expression), errors) = parse_recover(block(fresh_statement()), src) else { - panic!("Expected to be able to parse a block expression"); - }; + fn modifiers_not_followed_by_an_item(&mut self, modifiers: Modifiers) { + self.visibility_not_followed_by_an_item(modifiers); + self.unconstrained_not_followed_by_an_item(modifiers); + self.comptime_not_followed_by_an_item(modifiers); + } - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected an identifier after ."); + fn visibility_not_followed_by_an_item(&mut self, modifiers: Modifiers) { + if modifiers.visibility != ItemVisibility::Private { + self.push_error( + ParserErrorReason::VisibilityNotFollowedByAnItem { + visibility: modifiers.visibility, + }, + modifiers.visibility_span, + ); + } + } - let statement = &block_expression.statements[0]; - let StatementKind::Expression(expr) = &statement.kind else { - panic!("Expected an expression statement"); - }; + fn unconstrained_not_followed_by_an_item(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, span); + } + } - let ExpressionKind::Variable(var) = &expr.kind else { - panic!("Expected a variable expression"); - }; + fn comptime_not_followed_by_an_item(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, span); + } + } - assert_eq!(var.to_string(), "foo"); + fn comptime_mutable_and_unconstrained_not_applicable(&mut self, modifiers: Modifiers) { + self.mutable_not_applicable(modifiers); + self.comptime_not_applicable(modifiers); + self.unconstrained_not_applicable(modifiers); } - #[test] - fn parse_recover_impl_without_body() { - let src = "impl Foo"; + fn mutable_not_applicable(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.mutable { + self.push_error(ParserErrorReason::MutableNotApplicable, span); + } + } - let (top_level_statement, errors) = parse_recover(implementation(), src); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected <, where or { after impl type"); + fn comptime_not_applicable(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotApplicable, span); + } + } - let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatementKind::Impl(impl_) = top_level_statement else { - panic!("Expected to parse an impl"); - }; + fn unconstrained_not_applicable(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotApplicable, span); + } + } - assert_eq!(impl_.object_type.to_string(), "Foo"); - assert!(impl_.methods.is_empty()); + fn push_error(&mut self, reason: ParserErrorReason, span: Span) { + self.errors.push(ParserError::with_reason(reason, span)); } } + +fn eof_spanned_token() -> SpannedToken { + SpannedToken::new(Token::EOF, Default::default()) +} diff --git a/compiler/noirc_frontend/src/parser/parser/arguments.rs b/compiler/noirc_frontend/src/parser/parser/arguments.rs new file mode 100644 index 00000000000..380f42809a6 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -0,0 +1,40 @@ +use crate::{ast::Expression, token::Token}; + +use super::{parse_many::separated_by_comma_until_right_paren, Parser}; + +pub(crate) struct CallArguments { + pub(crate) arguments: Vec, + pub(crate) is_macro_call: bool, +} + +impl<'a> Parser<'a> { + /// Arguments = '(' ArgumentsList? ')' + /// + /// ArgumentsList = Expression ( ',' Expression )? ','? + pub(crate) fn parse_arguments(&mut self) -> Option> { + if !self.eat_left_paren() { + return None; + } + + let arguments = self.parse_many( + "arguments", + separated_by_comma_until_right_paren(), + Self::parse_expression_in_list, + ); + + Some(arguments) + } + + /// CallArguments = '!'? Arguments + pub(super) fn parse_call_arguments(&mut self) -> Option { + let is_macro_call = self.at(Token::Bang) && self.next_is(Token::LeftParen); + + if is_macro_call { + // Given that we expected '!' '(', it's safe to skip the '!' because the next + // `self.parse_arguments()` will always return `Some`. + self.bump(); + } + + self.parse_arguments().map(|arguments| CallArguments { arguments, is_macro_call }) + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/assertion.rs b/compiler/noirc_frontend/src/parser/parser/assertion.rs deleted file mode 100644 index 9eb429ef295..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/assertion.rs +++ /dev/null @@ -1,196 +0,0 @@ -use crate::ast::StatementKind; -use crate::parser::{ignore_then_commit, then_commit, ParserError, ParserErrorReason}; -use crate::parser::{labels::ParsingRuleLabel, parenthesized, ExprParser, NoirParser}; - -use crate::ast::{ConstrainKind, ConstrainStatement}; -use crate::token::{Keyword, Token}; - -use chumsky::prelude::*; - -use super::keyword; - -pub(super) fn constrain<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - ignore_then_commit( - keyword(Keyword::Constrain).labelled(ParsingRuleLabel::Statement), - expr_parser, - ) - .map_with_span(|expr, span| { - StatementKind::Constrain(ConstrainStatement { - kind: ConstrainKind::Constrain, - arguments: vec![expr], - span, - }) - }) - .validate(|expr, span, emit| { - emit(ParserError::with_reason(ParserErrorReason::ConstrainDeprecated, span)); - expr - }) -} - -pub(super) fn assertion<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - let keyword = choice(( - keyword(Keyword::Assert).map(|_| ConstrainKind::Assert), - keyword(Keyword::AssertEq).map(|_| ConstrainKind::AssertEq), - )); - - let argument_parser = expr_parser.separated_by(just(Token::Comma)).allow_trailing(); - - then_commit(keyword, parenthesized(argument_parser)) - .labelled(ParsingRuleLabel::Statement) - .map_with_span(|(kind, arguments), span| { - StatementKind::Constrain(ConstrainStatement { arguments, kind, span }) - }) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - ast::{BinaryOpKind, ExpressionKind, Literal}, - parser::parser::{ - expression, - test_helpers::{parse_all, parse_all_failing, parse_with}, - }, - }; - - /// Deprecated constrain usage test - #[test] - fn parse_constrain() { - let errors = parse_with(constrain(expression()), "constrain x == y").unwrap_err(); - assert_eq!(errors.len(), 1); - assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); - - // Currently we disallow constrain statements where the outer infix operator - // produces a value. This would require an implicit `==` which - // may not be intuitive to the user. - // - // If this is deemed useful, one would either apply a transformation - // or interpret it with an `==` in the evaluator - let disallowed_operators = vec![ - BinaryOpKind::And, - BinaryOpKind::Subtract, - BinaryOpKind::Divide, - BinaryOpKind::Multiply, - BinaryOpKind::Or, - ]; - - for operator in disallowed_operators { - let src = format!("constrain x {} y;", operator.as_string()); - let errors = parse_with(constrain(expression()), &src).unwrap_err(); - assert_eq!(errors.len(), 2); - assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); - } - - // These are general cases which should always work. - // - // The first case is the most noteworthy. It contains two `==` - // The first (inner) `==` is a predicate which returns 0/1 - // The outer layer is an infix `==` which is - // associated with the Constrain statement - let errors = parse_all_failing( - constrain(expression()), - vec![ - "constrain ((x + y) == k) + z == y", - "constrain (x + !y) == y", - "constrain (x ^ y) == y", - "constrain (x ^ y) == (y + m)", - "constrain x + x ^ x == y | m", - ], - ); - assert_eq!(errors.len(), 5); - assert!(errors - .iter() - .all(|err| { err.is_error() && err.to_string().contains("deprecated") })); - } - - /// This is the standard way to declare an assert statement - #[test] - fn parse_assert() { - parse_with(assertion(expression()), "assert(x == y)").unwrap(); - - // Currently we disallow constrain statements where the outer infix operator - // produces a value. This would require an implicit `==` which - // may not be intuitive to the user. - // - // If this is deemed useful, one would either apply a transformation - // or interpret it with an `==` in the evaluator - let disallowed_operators = vec![ - BinaryOpKind::And, - BinaryOpKind::Subtract, - BinaryOpKind::Divide, - BinaryOpKind::Multiply, - BinaryOpKind::Or, - ]; - - for operator in disallowed_operators { - let src = format!("assert(x {} y);", operator.as_string()); - parse_with(assertion(expression()), &src).unwrap_err(); - } - - // These are general cases which should always work. - // - // The first case is the most noteworthy. It contains two `==` - // The first (inner) `==` is a predicate which returns 0/1 - // The outer layer is an infix `==` which is - // associated with the Constrain statement - parse_all( - assertion(expression()), - vec![ - "assert(((x + y) == k) + z == y)", - "assert((x + !y) == y)", - "assert((x ^ y) == y)", - "assert((x ^ y) == (y + m))", - "assert(x + x ^ x == y | m)", - ], - ); - - match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() - { - StatementKind::Constrain(ConstrainStatement { arguments, .. }) => { - let message = arguments.last().unwrap(); - match &message.kind { - ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message"); - } - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } - - /// This is the standard way to assert that two expressions are equivalent - #[test] - fn parse_assert_eq() { - parse_all( - assertion(expression()), - vec![ - "assert_eq(x, y)", - "assert_eq(((x + y) == k) + z, y)", - "assert_eq(x + !y, y)", - "assert_eq(x ^ y, y)", - "assert_eq(x ^ y, y + m)", - "assert_eq(x + x ^ x, y | m)", - ], - ); - match parse_with(assertion(expression()), "assert_eq(x, y, \"assertion message\")").unwrap() - { - StatementKind::Constrain(ConstrainStatement { arguments, .. }) => { - let message = arguments.last().unwrap(); - match &message.kind { - ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message"); - } - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } -} diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index dc363248d72..d0f7221a6be 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,77 +1,81 @@ -use chumsky::Parser; use noirc_errors::Span; -use crate::{ - parser::{NoirParser, ParserError, ParserErrorReason}, - token::{Attribute, Attributes, SecondaryAttribute, Token, TokenKind}, -}; +use crate::parser::ParserErrorReason; +use crate::token::SecondaryAttribute; +use crate::token::{Attribute, Token, TokenKind}; -use super::primitives::token_kind; +use super::parse_many::without_separator; +use super::Parser; -fn attribute() -> impl NoirParser { - token_kind(TokenKind::Attribute).map(|token| match token { - Token::Attribute(attribute) => attribute, - _ => unreachable!("Parser should have already errored due to token not being an attribute"), - }) -} +impl<'a> Parser<'a> { + /// InnerAttribute = inner_attribute + pub(super) fn parse_inner_attribute(&mut self) -> Option { + let token = self.eat_kind(TokenKind::InnerAttribute)?; + match token.into_token() { + Token::InnerAttribute(attribute) => Some(attribute), + _ => unreachable!(), + } + } -pub(super) fn attributes() -> impl NoirParser> { - attribute().repeated() -} + /// Attributes = attribute* + pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Span)> { + self.parse_many("attributes", without_separator(), Self::parse_attribute) + } -pub(super) fn validate_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Attributes { - let mut primary = None; - let mut secondary = Vec::new(); + fn parse_attribute(&mut self) -> Option<(Attribute, Span)> { + self.eat_kind(TokenKind::Attribute).map(|token| match token.into_token() { + Token::Attribute(attribute) => (attribute, self.previous_token_span), + _ => unreachable!(), + }) + } - for attribute in attributes { - match attribute { - Attribute::Function(attr) => { - if primary.is_some() { - emit(ParserError::with_reason( - ParserErrorReason::MultipleFunctionAttributesFound, - span, - )); + pub(super) fn validate_secondary_attributes( + &mut self, + attributes: Vec<(Attribute, Span)>, + ) -> Vec { + attributes + .into_iter() + .filter_map(|(attribute, span)| match attribute { + Attribute::Function(..) => { + self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnStruct, span); + None } - primary = Some(attr); - } - Attribute::Secondary(attr) => secondary.push(attr), - } + Attribute::Secondary(attr) => Some(attr), + }) + .collect() } - - Attributes { function: primary, secondary } } -pub(super) fn validate_secondary_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Vec { - let mut struct_attributes = vec![]; +#[cfg(test)] +mod tests { + use crate::{ + parser::{parser::tests::expect_no_errors, Parser}, + token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, + }; - for attribute in attributes { - match attribute { - Attribute::Function(..) => { - emit(ParserError::with_reason( - ParserErrorReason::NoFunctionAttributesAllowedOnStruct, - span, - )); - } - Attribute::Secondary(attr) => struct_attributes.push(attr), - } + #[test] + fn parses_inner_attribute() { + let src = "#![hello]"; + let mut parser = Parser::for_str(src); + let Some(SecondaryAttribute::Custom(custom)) = parser.parse_inner_attribute() else { + panic!("Expected inner custom attribute"); + }; + expect_no_errors(&parser.errors); + assert_eq!(custom.contents, "hello"); } - struct_attributes -} + #[test] + fn parses_attributes() { + let src = "#[test] #[deprecated]"; + let mut parser = Parser::for_str(src); + let mut attributes = parser.parse_attributes(); + expect_no_errors(&parser.errors); + assert_eq!(attributes.len(), 2); + + let (attr, _) = attributes.remove(0); + assert!(matches!(attr, Attribute::Function(FunctionAttribute::Test(TestScope::None)))); -pub(super) fn inner_attribute() -> impl NoirParser { - token_kind(TokenKind::InnerAttribute).map(|token| match token { - Token::InnerAttribute(attribute) => attribute, - _ => unreachable!( - "Parser should have already errored due to token not being an inner attribute" - ), - }) + let (attr, _) = attributes.remove(0); + assert!(matches!(attr, Attribute::Secondary(SecondaryAttribute::Deprecated(None)))); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 151ff21017f..578a49641f6 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -1,36 +1,58 @@ -use chumsky::Parser; - -use crate::{ - parser::NoirParser, - token::{DocStyle, Token, TokenKind}, -}; - -use super::primitives::token_kind; - -fn outer_doc_comment() -> impl NoirParser { - token_kind(TokenKind::OuterDocComment).map(|token| match token { - Token::LineComment(comment, Some(DocStyle::Outer)) => comment, - Token::BlockComment(comment, Some(DocStyle::Outer)) => comment, - _ => unreachable!( - "Parser should have already errored due to token not being an outer doc comment" - ), - }) -} +use crate::token::{DocStyle, Token, TokenKind}; -pub(super) fn outer_doc_comments() -> impl NoirParser> { - outer_doc_comment().repeated() -} +use super::{parse_many::without_separator, Parser}; + +impl<'a> Parser<'a> { + /// InnerDocComments = inner_doc_comment* + pub(super) fn parse_inner_doc_comments(&mut self) -> Vec { + self.parse_many("inner doc comments", without_separator(), Self::parse_inner_doc_comment) + } + + fn parse_inner_doc_comment(&mut self) -> Option { + self.eat_kind(TokenKind::InnerDocComment).map(|token| match token.into_token() { + Token::LineComment(comment, Some(DocStyle::Inner)) + | Token::BlockComment(comment, Some(DocStyle::Inner)) => comment, + _ => unreachable!(), + }) + } -fn inner_doc_comment() -> impl NoirParser { - token_kind(TokenKind::InnerDocComment).map(|token| match token { - Token::LineComment(comment, Some(DocStyle::Inner)) => comment, - Token::BlockComment(comment, Some(DocStyle::Inner)) => comment, - _ => unreachable!( - "Parser should have already errored due to token not being an inner doc comment" - ), - }) + /// OuterDocComments = outer_doc_comments* + pub(super) fn parse_outer_doc_comments(&mut self) -> Vec { + self.parse_many("outer doc comments", without_separator(), Self::parse_outer_doc_comment) + } + + fn parse_outer_doc_comment(&mut self) -> Option { + self.eat_kind(TokenKind::OuterDocComment).map(|token| match token.into_token() { + Token::LineComment(comment, Some(DocStyle::Outer)) + | Token::BlockComment(comment, Some(DocStyle::Outer)) => comment, + _ => unreachable!(), + }) + } } -pub(super) fn inner_doc_comments() -> impl NoirParser> { - inner_doc_comment().repeated() +#[cfg(test)] +mod tests { + use crate::parser::{parser::tests::expect_no_errors, Parser}; + + #[test] + fn parses_inner_doc_comments() { + let src = "//! Hello\n//! World"; + let mut parser = Parser::for_str(src); + let comments = parser.parse_inner_doc_comments(); + expect_no_errors(&parser.errors); + assert_eq!(comments.len(), 2); + assert_eq!(comments[0], " Hello"); + assert_eq!(comments[1], " World"); + } + + #[test] + fn parses_outer_doc_comments() { + let src = "/// Hello\n/// World"; + let mut parser = Parser::for_str(src); + let comments = parser.parse_outer_doc_comments(); + expect_no_errors(&parser.errors); + assert_eq!(comments.len(), 2); + assert_eq!(comments[0], " Hello"); + assert_eq!(comments[1], " World"); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs new file mode 100644 index 00000000000..e2b942faebf --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -0,0 +1,1593 @@ +use iter_extended::vecmap; +use noirc_errors::Span; + +use crate::{ + ast::{ + ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, + Expression, ExpressionKind, GenericTypeArgs, Ident, IfExpression, IndexExpression, Literal, + MemberAccessExpression, MethodCallExpression, Statement, TypePath, UnaryOp, UnresolvedType, + }, + parser::{labels::ParsingRuleLabel, parser::parse_many::separated_by_comma, ParserErrorReason}, + token::{Keyword, Token, TokenKind}, +}; + +use super::{ + parse_many::{ + separated_by_comma_until_right_brace, separated_by_comma_until_right_paren, + without_separator, + }, + Parser, +}; + +impl<'a> Parser<'a> { + pub(crate) fn parse_expression_or_error(&mut self) -> Expression { + self.parse_expression_or_error_impl(true) // allow constructors + } + + /// Expression = EqualOrNotEqualExpression + pub(crate) fn parse_expression(&mut self) -> Option { + self.parse_expression_impl(true) // allow constructors + } + + /// When parsing `if` conditions we don't allow constructors. + /// For example `if foo { 1 }` shouldn't have `foo { 1 }` as the condition, but `foo` instead. + /// The same goes with `for`: `for x in foo { 1 }` should have `foo` as the collection, not `foo { 1 }`. + /// + /// ExpressionExceptConstructor = "Expression except ConstructorException" + pub(crate) fn parse_expression_except_constructor_or_error(&mut self) -> Expression { + self.parse_expression_or_error_impl(false) // allow constructors + } + + pub(crate) fn parse_expression_or_error_impl( + &mut self, + allow_constructors: bool, + ) -> Expression { + if let Some(expr) = self.parse_expression_impl(allow_constructors) { + expr + } else { + self.push_expected_expression(); + Expression { kind: ExpressionKind::Error, span: self.span_at_previous_token_end() } + } + } + + fn parse_expression_impl(&mut self, allow_constructors: bool) -> Option { + self.parse_equal_or_not_equal(allow_constructors) + } + + /// Term + /// = UnaryOp Term + /// | AtomOrUnaryRightExpression + pub(super) fn parse_term(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + + if let Some(operator) = self.parse_unary_op() { + let Some(rhs) = self.parse_term(allow_constructors) else { + self.expected_label(ParsingRuleLabel::Expression); + return None; + }; + let kind = ExpressionKind::prefix(operator, rhs); + let span = self.span_since(start_span); + return Some(Expression { kind, span }); + } + + self.parse_atom_or_unary_right(allow_constructors) + } + + /// UnaryOp = '&' 'mut' | '-' | '!' | '*' + fn parse_unary_op(&mut self) -> Option { + if self.at(Token::Ampersand) && self.next_is(Token::Keyword(Keyword::Mut)) { + self.bump(); + self.bump(); + Some(UnaryOp::MutableReference) + } else if self.eat(Token::Minus) { + Some(UnaryOp::Minus) + } else if self.eat(Token::Bang) { + Some(UnaryOp::Not) + } else if self.eat(Token::Star) { + Some(UnaryOp::Dereference { implicitly_added: false }) + } else { + None + } + } + + /// AtomOrUnaryRightExpression + /// = Atom + /// | UnaryRightExpression + fn parse_atom_or_unary_right(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let mut atom = self.parse_atom(allow_constructors)?; + let mut parsed; + + loop { + (atom, parsed) = self.parse_unary_right(atom, start_span); + if parsed { + continue; + } else { + break; + } + } + + Some(atom) + } + + /// UnaryRightExpression + /// = CallExpression + /// | MemberAccessOrMethodCallExpression + /// | CastExpression + /// | IndexExpression + fn parse_unary_right(&mut self, mut atom: Expression, start_span: Span) -> (Expression, bool) { + let mut parsed; + + (atom, parsed) = self.parse_call(atom, start_span); + if parsed { + return (atom, parsed); + } + + (atom, parsed) = self.parse_member_access_or_method_call(atom, start_span); + if parsed { + return (atom, parsed); + } + + (atom, parsed) = self.parse_cast(atom, start_span); + if parsed { + return (atom, parsed); + } + + self.parse_index(atom, start_span) + } + + /// CallExpression = Atom CallArguments + fn parse_call(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + if let Some(call_arguments) = self.parse_call_arguments() { + let kind = ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(atom), + arguments: call_arguments.arguments, + is_macro_call: call_arguments.is_macro_call, + })); + let span = self.span_since(start_span); + let atom = Expression { kind, span }; + (atom, true) + } else { + (atom, false) + } + } + + /// MemberAccessOrMethodCallExpression + /// = MemberAccessExpression + /// | MethodCallExpression + /// + /// MemberAccessExpression = Atom '.' identifier + /// + /// MethodCallExpression = Atom '.' identifier CallArguments + fn parse_member_access_or_method_call( + &mut self, + atom: Expression, + start_span: Span, + ) -> (Expression, bool) { + if !self.eat_dot() { + return (atom, false); + } + + let Some(field_name) = self.parse_member_access_field_name() else { return (atom, true) }; + + let generics = self.parse_generics_after_member_access_field_name(); + + let kind = if let Some(call_arguments) = self.parse_call_arguments() { + ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: atom, + method_name: field_name, + generics, + arguments: call_arguments.arguments, + is_macro_call: call_arguments.is_macro_call, + })) + } else { + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: atom, + rhs: field_name, + })) + }; + + let span = self.span_since(start_span); + let atom = Expression { kind, span }; + (atom, true) + } + + fn parse_member_access_field_name(&mut self) -> Option { + if let Some(ident) = self.eat_ident() { + Some(ident) + } else if let Some(int) = self.eat_int() { + Some(Ident::new(int.to_string(), self.previous_token_span)) + } else { + self.push_error( + ParserErrorReason::ExpectedFieldName(self.token.token().clone()), + self.current_token_span, + ); + None + } + } + + /// CastExpression = Atom 'as' Type + fn parse_cast(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + if !self.eat_keyword(Keyword::As) { + return (atom, false); + } + + let typ = self.parse_type_or_error(); + let kind = ExpressionKind::Cast(Box::new(CastExpression { lhs: atom, r#type: typ })); + let span = self.span_since(start_span); + let atom = Expression { kind, span }; + (atom, true) + } + + /// IndexExpression = Atom '[' Expression ']' + fn parse_index(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + if !self.eat_left_bracket() { + return (atom, false); + } + + let index = self.parse_expression_or_error(); + self.eat_or_error(Token::RightBracket); + let kind = ExpressionKind::Index(Box::new(IndexExpression { collection: atom, index })); + let span = self.span_since(start_span); + let atom = Expression { kind, span }; + (atom, true) + } + + fn parse_generics_after_member_access_field_name(&mut self) -> Option> { + if self.eat_double_colon() { + let generics = + self.parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInMethodCalls); + if generics.is_none() { + self.expected_token(Token::Less); + } + generics + } else { + None + } + } + + /// Atom + /// = Literal + /// | ParenthesesExpression + /// | UnsafeExpression + /// | PathExpression + /// | IfExpression + /// | Lambda + /// | ComptimeExpression + /// | UnquoteExpression + /// | TypePathExpression + /// | AsTraitPath + /// | ResolvedExpression + /// | InternedExpression + /// | InternedStatementExpression + fn parse_atom(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let kind = self.parse_atom_kind(allow_constructors)?; + Some(Expression { kind, span: self.span_since(start_span) }) + } + + fn parse_atom_kind(&mut self, allow_constructors: bool) -> Option { + if let Some(literal) = self.parse_literal() { + return Some(literal); + } + + if let Some(kind) = self.parse_parentheses_expression() { + return Some(kind); + } + + if let Some(kind) = self.parse_unsafe_expr() { + return Some(kind); + } + + if let Some(kind) = self.parse_path_expr(allow_constructors) { + return Some(kind); + } + + // A constructor where the type is an interned unresolved type data is valid + if matches!(self.token.token(), Token::InternedUnresolvedTypeData(..)) + && self.next_is(Token::LeftBrace) + { + let span = self.current_token_span; + let typ = self.parse_interned_type().unwrap(); + self.eat_or_error(Token::LeftBrace); + let typ = UnresolvedType { typ, span }; + return Some(self.parse_constructor(typ)); + } + + if let Some(kind) = self.parse_if_expr() { + return Some(kind); + } + + if let Some(kind) = self.parse_lambda() { + return Some(kind); + } + + if let Some(kind) = self.parse_comptime_expr() { + return Some(kind); + } + + if let Some(kind) = self.parse_unquote_expr() { + return Some(kind); + } + + if let Some(kind) = self.parse_type_path_expr() { + return Some(kind); + } + + if let Some(as_trait_path) = self.parse_as_trait_path() { + return Some(ExpressionKind::AsTraitPath(as_trait_path)); + } + + if let Some(kind) = self.parse_resolved_expr() { + return Some(kind); + } + + if let Some(kind) = self.parse_interned_expr() { + return Some(kind); + } + + if let Some(kind) = self.parse_interned_statement_expr() { + return Some(kind); + } + + None + } + + /// ResolvedExpression = unquote_marker + fn parse_resolved_expr(&mut self) -> Option { + if let Some(token) = self.eat_kind(TokenKind::UnquoteMarker) { + match token.into_token() { + Token::UnquoteMarker(expr_id) => return Some(ExpressionKind::Resolved(expr_id)), + _ => unreachable!(""), + } + } + + None + } + + /// InternedExpression = interned_expr + fn parse_interned_expr(&mut self) -> Option { + if let Some(token) = self.eat_kind(TokenKind::InternedExpr) { + match token.into_token() { + Token::InternedExpr(id) => return Some(ExpressionKind::Interned(id)), + _ => unreachable!(""), + } + } + + None + } + + /// InternedStatementExpression = interned_statement + fn parse_interned_statement_expr(&mut self) -> Option { + if let Some(token) = self.eat_kind(TokenKind::InternedStatement) { + match token.into_token() { + Token::InternedStatement(id) => return Some(ExpressionKind::InternedStatement(id)), + _ => unreachable!(""), + } + } + + None + } + + /// UnsafeExpression = 'unsafe' Block + fn parse_unsafe_expr(&mut self) -> Option { + if !self.eat_keyword(Keyword::Unsafe) { + return None; + } + + let start_span = self.current_token_span; + if let Some(block) = self.parse_block() { + Some(ExpressionKind::Unsafe(block, self.span_since(start_span))) + } else { + Some(ExpressionKind::Error) + } + } + + /// PathExpression + /// = VariableExpression + /// | ConstructorExpression + /// + /// VariableExpression = Path + fn parse_path_expr(&mut self, allow_constructors: bool) -> Option { + let Some(path) = self.parse_path() else { + return None; + }; + + if allow_constructors && self.eat_left_brace() { + let typ = UnresolvedType::from_path(path); + return Some(self.parse_constructor(typ)); + } + + Some(ExpressionKind::Variable(path)) + } + + /// ConstructorExpression = Type '{' ConstructorFields? '}' + /// + /// ConstructorFields = ConstructorField ( ',' ConstructorField )* ','? + /// + /// ConstructorField = identifier ( ':' Expression )? + fn parse_constructor(&mut self, typ: UnresolvedType) -> ExpressionKind { + let fields = self.parse_many( + "constructor fields", + separated_by_comma_until_right_brace(), + Self::parse_constructor_field, + ); + + ExpressionKind::Constructor(Box::new(ConstructorExpression { + typ, + fields, + struct_type: None, + })) + } + + fn parse_constructor_field(&mut self) -> Option<(Ident, Expression)> { + let Some(ident) = self.eat_ident() else { + return None; + }; + + Some(if self.eat_colon() { + let expression = self.parse_expression_or_error(); + (ident, expression) + } else { + (ident.clone(), ident.into()) + }) + } + + /// IfExpression = 'if' ExpressionExceptConstructor Block ( 'else' ( Block | IfExpression ) )? + pub(super) fn parse_if_expr(&mut self) -> Option { + if !self.eat_keyword(Keyword::If) { + return None; + } + + let condition = self.parse_expression_except_constructor_or_error(); + + let start_span = self.current_token_span; + let Some(consequence) = self.parse_block() else { + self.expected_token(Token::LeftBrace); + let span = self.span_at_previous_token_end(); + return Some(ExpressionKind::If(Box::new(IfExpression { + condition, + consequence: Expression { kind: ExpressionKind::Error, span }, + alternative: None, + }))); + }; + let span = self.span_since(start_span); + let consequence = Expression { kind: ExpressionKind::Block(consequence), span }; + + let alternative = if self.eat_keyword(Keyword::Else) { + let start_span = self.current_token_span; + if let Some(block) = self.parse_block() { + let span = self.span_since(start_span); + Some(Expression { kind: ExpressionKind::Block(block), span }) + } else if let Some(if_expr) = self.parse_if_expr() { + Some(Expression { kind: if_expr, span: self.span_since(start_span) }) + } else { + self.expected_token(Token::LeftBrace); + None + } + } else { + None + }; + + Some(ExpressionKind::If(Box::new(IfExpression { condition, consequence, alternative }))) + } + + /// ComptimeExpression = 'comptime' Block + fn parse_comptime_expr(&mut self) -> Option { + if !self.eat_keyword(Keyword::Comptime) { + return None; + } + + let start_span = self.current_token_span; + + let Some(block) = self.parse_block() else { + self.expected_token(Token::LeftBrace); + return None; + }; + + Some(ExpressionKind::Comptime(block, self.span_since(start_span))) + } + + /// UnquoteExpression + /// = '$' identifier + /// | '$' '(' Expression ')' + fn parse_unquote_expr(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.eat(Token::DollarSign) { + return None; + } + + if let Some(path) = self.parse_path() { + let expr = Expression { + kind: ExpressionKind::Variable(path), + span: self.span_since(start_span), + }; + return Some(ExpressionKind::Unquote(Box::new(expr))); + } + + let span_at_left_paren = self.current_token_span; + if self.eat_left_paren() { + let expr = self.parse_expression_or_error(); + self.eat_or_error(Token::RightParen); + let expr = Expression { + kind: ExpressionKind::Parenthesized(Box::new(expr)), + span: self.span_since(span_at_left_paren), + }; + return Some(ExpressionKind::Unquote(Box::new(expr))); + } + + self.push_error( + ParserErrorReason::ExpectedIdentifierOrLeftParenAfterDollar, + self.current_token_span, + ); + + None + } + + /// TypePathExpression = PrimitiveType '::' identifier ( '::' GenericTypeArgs )? + fn parse_type_path_expr(&mut self) -> Option { + let start_span = self.current_token_span; + let Some(typ) = self.parse_primitive_type() else { + return None; + }; + let typ = UnresolvedType { typ, span: self.span_since(start_span) }; + + self.eat_or_error(Token::DoubleColon); + + let item = if let Some(ident) = self.eat_ident() { + ident + } else { + self.expected_identifier(); + Ident::new(String::new(), self.span_at_previous_token_end()) + }; + + let turbofish = if self.eat_double_colon() { + let generics = self.parse_generic_type_args(); + if generics.is_empty() { + self.expected_token(Token::Less); + } + generics + } else { + GenericTypeArgs::default() + }; + + Some(ExpressionKind::TypePath(TypePath { typ, item, turbofish })) + } + + /// Literal + /// = bool + /// | int + /// | str + /// | rawstr + /// | fmtstr + /// | QuoteExpression + /// | ArrayExpression + /// | SliceExpression + /// | BlockExpression + /// + /// QuoteExpression = 'quote' '{' token* '}' + /// + /// ArrayExpression = ArrayLiteral + /// + /// BlockExpression = Block + fn parse_literal(&mut self) -> Option { + if let Some(bool) = self.eat_bool() { + return Some(ExpressionKind::Literal(Literal::Bool(bool))); + } + + if let Some(int) = self.eat_int() { + return Some(ExpressionKind::integer(int)); + } + + if let Some(string) = self.eat_str() { + return Some(ExpressionKind::Literal(Literal::Str(string))); + } + + if let Some((string, n)) = self.eat_raw_str() { + return Some(ExpressionKind::Literal(Literal::RawStr(string, n))); + } + + if let Some(string) = self.eat_fmt_str() { + return Some(ExpressionKind::Literal(Literal::FmtStr(string))); + } + + if let Some(tokens) = self.eat_quote() { + return Some(ExpressionKind::Quote(tokens)); + } + + if let Some(literal) = self.parse_array_literal() { + return Some(ExpressionKind::Literal(Literal::Array(literal))); + } + + if let Some(literal) = self.parse_slice_literal() { + return Some(ExpressionKind::Literal(Literal::Slice(literal))); + } + + if let Some(kind) = self.parse_block() { + return Some(ExpressionKind::Block(kind)); + } + + None + } + + /// ArrayLiteral + /// = StandardArrayLiteral + /// | RepeatedArrayLiteral + /// + /// StandardArrayLiteral = '[' ArrayElements? ']' + /// + /// ArrayElements = Expression ( ',' Expression )? ','? + /// + /// RepeatedArrayLiteral = '[' Expression ';' TypeExpression ']' + fn parse_array_literal(&mut self) -> Option { + if !self.eat_left_bracket() { + return None; + } + + if self.eat_right_bracket() { + return Some(ArrayLiteral::Standard(Vec::new())); + } + + let first_expr = self.parse_expression_or_error(); + if first_expr.kind == ExpressionKind::Error { + return Some(ArrayLiteral::Standard(Vec::new())); + } + + if self.eat_semicolon() { + let length = self.parse_expression_or_error(); + self.eat_or_error(Token::RightBracket); + return Some(ArrayLiteral::Repeated { + repeated_element: Box::new(first_expr), + length: Box::new(length), + }); + } + + let comma_after_first_expr = self.eat_comma(); + let second_expr_span = self.current_token_span; + + let mut exprs = self.parse_many( + "expressions", + separated_by_comma().until(Token::RightBracket), + Self::parse_expression_in_list, + ); + + if !exprs.is_empty() && !comma_after_first_expr { + self.expected_token_separating_items(Token::Comma, "expressions", second_expr_span); + } + + exprs.insert(0, first_expr); + + Some(ArrayLiteral::Standard(exprs)) + } + + /// SliceExpression = '&' ArrayLiteral + fn parse_slice_literal(&mut self) -> Option { + if !(self.at(Token::Ampersand) && self.next_is(Token::LeftBracket)) { + return None; + } + + self.bump(); + self.parse_array_literal() + } + + /// ParenthesesExpression + /// = UnitLiteral + /// | ParenthesizedExpression + /// | TupleExpression + /// + /// UnitLiteral = '(' ')' + /// + /// ParenthesizedExpression = '(' Expression ')' + /// + /// TupleExpression = '(' Expression ( ',' Expression )+ ','? ')' + fn parse_parentheses_expression(&mut self) -> Option { + if !self.eat_left_paren() { + return None; + } + + if self.eat_right_paren() { + return Some(ExpressionKind::Literal(Literal::Unit)); + } + + let (mut exprs, trailing_comma) = self.parse_many_return_trailing_separator_if_any( + "expressions", + separated_by_comma_until_right_paren(), + Self::parse_expression_in_list, + ); + + Some(if exprs.len() == 1 && !trailing_comma { + ExpressionKind::Parenthesized(Box::new(exprs.remove(0))) + } else { + ExpressionKind::Tuple(exprs) + }) + } + + pub(super) fn parse_expression_in_list(&mut self) -> Option { + if let Some(expr) = self.parse_expression() { + Some(expr) + } else { + self.expected_label(ParsingRuleLabel::Expression); + None + } + } + + /// Block = '{' Statement* '}' + pub(super) fn parse_block(&mut self) -> Option { + if !self.eat_left_brace() { + return None; + } + + let statements = self.parse_many( + "statements", + without_separator().until(Token::RightBrace), + Self::parse_statement_in_block, + ); + + let statements = self.check_statements_require_semicolon(statements); + + Some(BlockExpression { statements }) + } + + fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Span))> { + if let Some(statement) = self.parse_statement() { + Some(statement) + } else { + self.expected_label(ParsingRuleLabel::Statement); + None + } + } + + fn check_statements_require_semicolon( + &mut self, + statements: Vec<(Statement, (Option, Span))>, + ) -> Vec { + let last = statements.len().saturating_sub(1); + let iter = statements.into_iter().enumerate(); + vecmap(iter, |(i, (statement, (semicolon, span)))| { + statement + .add_semicolon(semicolon, span, i == last, &mut |error| self.errors.push(error)) + }) + } + + pub(super) fn push_expected_expression(&mut self) { + self.expected_label(ParsingRuleLabel::Expression); + } +} + +#[cfg(test)] +mod tests { + use strum::IntoEnumIterator; + + use crate::{ + ast::{ + ArrayLiteral, BinaryOpKind, Expression, ExpressionKind, Literal, StatementKind, + UnaryOp, UnresolvedTypeData, + }, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + token::Token, + }; + + fn parse_expression_no_errors(src: &str) -> Expression { + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert_eq!(expr.span.end() as usize, src.len()); + expect_no_errors(&parser.errors); + expr + } + + #[test] + fn parses_bool_literals() { + let src = "true"; + let expr = parse_expression_no_errors(src); + assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(true)))); + + let src = "false"; + let expr = parse_expression_no_errors(src); + assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(false)))); + } + + #[test] + fn parses_integer_literal() { + let src = "42"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + panic!("Expected integer literal"); + }; + assert_eq!(field, 42_u128.into()); + assert!(!negative); + } + + #[test] + fn parses_negative_integer_literal() { + let src = "-42"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + panic!("Expected integer literal"); + }; + assert_eq!(field, 42_u128.into()); + assert!(negative); + } + + #[test] + fn parses_parenthesized_expression() { + let src = "(42)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Parenthesized(expr) = expr.kind else { + panic!("Expected parenthesized expression"); + }; + let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + panic!("Expected integer literal"); + }; + assert_eq!(field, 42_u128.into()); + assert!(!negative); + } + + #[test] + fn parses_unit() { + let src = "()"; + let expr = parse_expression_no_errors(src); + assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Unit))); + } + + #[test] + fn parses_str() { + let src = "\"hello\""; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Str(string)) = expr.kind else { + panic!("Expected string literal"); + }; + assert_eq!(string, "hello"); + } + + #[test] + fn parses_raw_str() { + let src = "r#\"hello\"#"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::RawStr(string, n)) = expr.kind else { + panic!("Expected raw string literal"); + }; + assert_eq!(string, "hello"); + assert_eq!(n, 1); + } + + #[test] + fn parses_fmt_str() { + let src = "f\"hello\""; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::FmtStr(string)) = expr.kind else { + panic!("Expected format string literal"); + }; + assert_eq!(string, "hello"); + } + + #[test] + fn parses_tuple_expression() { + let src = "(1, 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Tuple(mut exprs) = expr.kind else { + panic!("Expected tuple expression"); + }; + assert_eq!(exprs.len(), 2); + + let expr = exprs.remove(0); + let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + panic!("Expected integer literal"); + }; + assert_eq!(field, 1_u128.into()); + assert!(!negative); + + let expr = exprs.remove(0); + let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + panic!("Expected integer literal"); + }; + assert_eq!(field, 2_u128.into()); + assert!(!negative); + } + + #[test] + fn parses_block_expression_with_a_single_expression() { + let src = "{ 1 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Block(mut block) = expr.kind else { + panic!("Expected block expression"); + }; + assert_eq!(block.statements.len(), 1); + + let statement = block.statements.remove(0); + let StatementKind::Expression(expr) = statement.kind else { + panic!("Expected expression statement"); + }; + + let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + panic!("Expected integer literal"); + }; + assert_eq!(field, 1_u128.into()); + assert!(!negative); + } + + #[test] + fn parses_block_expression_with_multiple_statements() { + let src = " + { + let x = 1; + let y = 2; + 3 + }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Block(block) = expr.kind else { + panic!("Expected block expression"); + }; + assert_eq!(block.statements.len(), 3); + assert_eq!(block.statements[0].kind.to_string(), "let x = 1"); + assert_eq!(block.statements[1].kind.to_string(), "let y = 2"); + assert_eq!(block.statements[2].kind.to_string(), "3"); + } + + #[test] + fn parses_block_expression_adds_semicolons() { + let src = " + { + 1 + 2 + 3 + }"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(parser.errors.len(), 2); + assert!(matches!( + parser.errors[0].reason(), + Some(ParserErrorReason::MissingSeparatingSemi) + )); + assert!(matches!( + parser.errors[1].reason(), + Some(ParserErrorReason::MissingSeparatingSemi) + )); + let ExpressionKind::Block(block) = expr.kind else { + panic!("Expected block expression"); + }; + assert_eq!(block.statements.len(), 3); + } + + #[test] + fn parses_unsafe_expression() { + let src = "unsafe { 1 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Unsafe(block, _) = expr.kind else { + panic!("Expected unsafe expression"); + }; + assert_eq!(block.statements.len(), 1); + } + + #[test] + fn parses_unclosed_parentheses() { + let src = " + ( + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + parser.parse_expression(); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected an expression but found end of input"); + } + + #[test] + fn parses_missing_comma_in_tuple() { + let src = " + (1 2) + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + parser.parse_expression(); + let reason = get_single_error_reason(&parser.errors, span); + let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { + panic!("Expected a different error"); + }; + assert_eq!(token, &Token::Comma); + assert_eq!(*items, "expressions"); + } + + #[test] + fn parses_empty_array_expression() { + let src = "[]"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind + else { + panic!("Expected array literal"); + }; + assert!(exprs.is_empty()); + } + + #[test] + fn parses_array_expression_with_one_element() { + let src = "[1]"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind + else { + panic!("Expected array literal"); + }; + assert_eq!(exprs.len(), 1); + assert_eq!(exprs[0].to_string(), "1"); + } + + #[test] + fn parses_array_expression_with_two_elements() { + let src = "[1, 3]"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind + else { + panic!("Expected array literal"); + }; + assert_eq!(exprs.len(), 2); + assert_eq!(exprs[0].to_string(), "1"); + assert_eq!(exprs[1].to_string(), "3"); + } + + #[test] + fn parses_array_expression_with_two_elements_missing_comma() { + let src = " + [1 3] + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert_eq!(expr.span.end() as usize, src.len()); + + let reason = get_single_error_reason(&parser.errors, span); + let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { + panic!("Expected a different error"); + }; + assert_eq!(token, &Token::Comma); + assert_eq!(*items, "expressions"); + + let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind + else { + panic!("Expected array literal"); + }; + assert_eq!(exprs.len(), 2); + assert_eq!(exprs[0].to_string(), "1"); + assert_eq!(exprs[1].to_string(), "3"); + } + + #[test] + fn parses_repeated_array_expression() { + let src = "[1; 10]"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { + repeated_element, + length, + })) = expr.kind + else { + panic!("Expected array literal"); + }; + assert_eq!(repeated_element.to_string(), "1"); + assert_eq!(length.to_string(), "10"); + } + + #[test] + fn parses_empty_slice_expression() { + let src = "&[]"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr.kind + else { + panic!("Expected slice literal"); + }; + assert!(exprs.is_empty()); + } + + #[test] + fn parses_variable_ident() { + let src = "foo"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Variable(path) = expr.kind else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "foo"); + } + + #[test] + fn parses_variable_path() { + let src = "foo::bar"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Variable(path) = expr.kind else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "foo::bar"); + } + + #[test] + fn parses_variable_path_with_turbofish() { + let src = "foo::<9>"; + parse_expression_no_errors(src); + } + + #[test] + fn parses_mutable_ref() { + let src = "&mut foo"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Prefix(prefix) = expr.kind else { + panic!("Expected prefix expression"); + }; + assert!(matches!(prefix.operator, UnaryOp::MutableReference)); + + let ExpressionKind::Variable(path) = prefix.rhs.kind else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "foo"); + } + + #[test] + fn parses_minus() { + let src = "-foo"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Prefix(prefix) = expr.kind else { + panic!("Expected prefix expression"); + }; + assert!(matches!(prefix.operator, UnaryOp::Minus)); + + let ExpressionKind::Variable(path) = prefix.rhs.kind else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "foo"); + } + + #[test] + fn parses_not() { + let src = "!foo"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Prefix(prefix) = expr.kind else { + panic!("Expected prefix expression"); + }; + assert!(matches!(prefix.operator, UnaryOp::Not)); + + let ExpressionKind::Variable(path) = prefix.rhs.kind else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "foo"); + } + + #[test] + fn parses_dereference() { + let src = "*foo"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Prefix(prefix) = expr.kind else { + panic!("Expected prefix expression"); + }; + assert!(matches!(prefix.operator, UnaryOp::Dereference { implicitly_added: false })); + + let ExpressionKind::Variable(path) = prefix.rhs.kind else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "foo"); + } + + #[test] + fn parses_quote() { + let src = "quote { 1 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Quote(tokens) = expr.kind else { + panic!("Expected quote expression"); + }; + assert_eq!(tokens.0.len(), 1); + } + + #[test] + fn parses_call() { + let src = "foo(1, 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Call(call) = expr.kind else { + panic!("Expected call expression"); + }; + assert_eq!(call.func.to_string(), "foo"); + assert_eq!(call.arguments.len(), 2); + assert!(!call.is_macro_call); + } + + #[test] + fn parses_call_missing_comma() { + let src = " + foo(1 2) + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert_eq!(expr.span.end() as usize, src.len()); + let reason = get_single_error_reason(&parser.errors, span); + let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { + panic!("Expected a different error"); + }; + assert_eq!(token, &Token::Comma); + assert_eq!(*items, "arguments"); + + let ExpressionKind::Call(call) = expr.kind else { + panic!("Expected call expression"); + }; + assert_eq!(call.func.to_string(), "foo"); + assert_eq!(call.arguments.len(), 2); + assert!(!call.is_macro_call); + } + + #[test] + fn parses_call_with_wrong_expression() { + let src = "foo(]) "; + let mut parser = Parser::for_str(src); + parser.parse_expression_or_error(); + assert!(!parser.errors.is_empty()); + } + + #[test] + fn parses_call_with_turbofish() { + let src = "foo::(1, 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Call(call) = expr.kind else { + panic!("Expected call expression"); + }; + assert_eq!(call.func.to_string(), "foo::"); + assert_eq!(call.arguments.len(), 2); + assert!(!call.is_macro_call); + } + + #[test] + fn parses_macro_call() { + let src = "foo!(1, 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Call(call) = expr.kind else { + panic!("Expected call expression"); + }; + assert_eq!(call.func.to_string(), "foo"); + assert_eq!(call.arguments.len(), 2); + assert!(call.is_macro_call); + } + + #[test] + fn parses_member_access() { + let src = "foo.bar"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::MemberAccess(member_access) = expr.kind else { + panic!("Expected member access expression"); + }; + assert_eq!(member_access.lhs.to_string(), "foo"); + assert_eq!(member_access.rhs.to_string(), "bar"); + } + + #[test] + fn parses_method_call() { + let src = "foo.bar(1, 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::MethodCall(method_call) = expr.kind else { + panic!("Expected method call expression"); + }; + assert_eq!(method_call.object.to_string(), "foo"); + assert_eq!(method_call.method_name.to_string(), "bar"); + assert!(!method_call.is_macro_call); + assert_eq!(method_call.arguments.len(), 2); + assert!(method_call.generics.is_none()); + } + + #[test] + fn parses_method_call_with_turbofish() { + let src = "foo.bar::(1, 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::MethodCall(method_call) = expr.kind else { + panic!("Expected method call expression"); + }; + assert_eq!(method_call.object.to_string(), "foo"); + assert_eq!(method_call.method_name.to_string(), "bar"); + assert!(!method_call.is_macro_call); + assert_eq!(method_call.arguments.len(), 2); + assert_eq!(method_call.generics.unwrap().len(), 2); + } + + #[test] + fn parses_method_macro_call() { + let src = "foo.bar!(1, 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::MethodCall(method_call) = expr.kind else { + panic!("Expected method call expression"); + }; + assert_eq!(method_call.object.to_string(), "foo"); + assert_eq!(method_call.method_name.to_string(), "bar"); + assert!(method_call.is_macro_call); + assert_eq!(method_call.arguments.len(), 2); + assert!(method_call.generics.is_none()); + } + + #[test] + fn parses_empty_constructor() { + let src = "Foo {}"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Constructor(constructor) = expr.kind else { + panic!("Expected constructor"); + }; + assert_eq!(constructor.typ.to_string(), "Foo"); + assert!(constructor.fields.is_empty()); + } + + #[test] + fn parses_constructor_with_fields() { + let src = "Foo { x: 1, y, z: 2 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Constructor(mut constructor) = expr.kind else { + panic!("Expected constructor"); + }; + assert_eq!(constructor.typ.to_string(), "Foo"); + assert_eq!(constructor.fields.len(), 3); + + let (name, expr) = constructor.fields.remove(0); + assert_eq!(name.to_string(), "x"); + assert_eq!(expr.to_string(), "1"); + + let (name, expr) = constructor.fields.remove(0); + assert_eq!(name.to_string(), "y"); + assert_eq!(expr.to_string(), "y"); + + let (name, expr) = constructor.fields.remove(0); + assert_eq!(name.to_string(), "z"); + assert_eq!(expr.to_string(), "2"); + } + + #[test] + fn parses_parses_if_true() { + let src = "if true { 1 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::If(if_expr) = expr.kind else { + panic!("Expected if"); + }; + assert_eq!(if_expr.condition.to_string(), "true"); + let ExpressionKind::Block(block_expr) = if_expr.consequence.kind else { + panic!("Expected block"); + }; + assert_eq!(block_expr.statements.len(), 1); + assert_eq!(block_expr.statements[0].kind.to_string(), "1"); + assert!(if_expr.alternative.is_none()); + } + + #[test] + fn parses_parses_if_var() { + let src = "if foo { 1 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::If(if_expr) = expr.kind else { + panic!("Expected if"); + }; + assert_eq!(if_expr.condition.to_string(), "foo"); + } + + #[test] + fn parses_parses_if_else() { + let src = "if true { 1 } else { 2 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::If(if_expr) = expr.kind else { + panic!("Expected if"); + }; + assert_eq!(if_expr.condition.to_string(), "true"); + assert!(if_expr.alternative.is_some()); + } + + #[test] + fn parses_parses_if_else_if() { + let src = "if true { 1 } else if false { 2 } else { 3 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::If(if_expr) = expr.kind else { + panic!("Expected if"); + }; + assert_eq!(if_expr.condition.to_string(), "true"); + let ExpressionKind::If(..) = if_expr.alternative.unwrap().kind else { + panic!("Expected if"); + }; + } + + #[test] + fn parses_cast() { + let src = "1 as u8"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Cast(cast_expr) = expr.kind else { + panic!("Expected cast"); + }; + assert_eq!(cast_expr.lhs.to_string(), "1"); + assert_eq!(cast_expr.r#type.to_string(), "u8"); + } + + #[test] + fn parses_cast_missing_type() { + let src = " + 1 as + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + parser.parse_expression(); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a type but found end of input"); + } + + #[test] + fn parses_index() { + let src = "1[2]"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Index(index_expr) = expr.kind else { + panic!("Expected index"); + }; + assert_eq!(index_expr.collection.to_string(), "1"); + assert_eq!(index_expr.index.to_string(), "2"); + } + + #[test] + fn parses_operators() { + for operator in BinaryOpKind::iter() { + let src = format!("1 {operator} 2"); + let mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert_eq!(expr.span.end() as usize, src.len()); + assert!(parser.errors.is_empty(), "Expected no errors for {operator}"); + let ExpressionKind::Infix(infix_expr) = expr.kind else { + panic!("Expected infix for {operator}"); + }; + assert_eq!(infix_expr.lhs.to_string(), "1"); + assert_eq!(infix_expr.operator.contents, operator); + assert_eq!(infix_expr.rhs.to_string(), "2"); + } + } + + #[test] + fn parses_operator_precedence() { + // This test produces a gigantic expression with lots of infix expressions without parentheses. + // We parse it, then we transform that to a string. Because `InfixExpression::to_string()` adds parentheses + // around it, we can check the operator precedence is correct by checking where parentheses were placed. + let multiply_or_divide_or_modulo = "1 * 2 / 3 % 4"; + let expected_multiply_or_divide_or_modulo = "(((1 * 2) / 3) % 4)"; + + let add_or_subtract = format!("{multiply_or_divide_or_modulo} + {multiply_or_divide_or_modulo} - {multiply_or_divide_or_modulo}"); + let expected_add_or_subtract = format!("(({expected_multiply_or_divide_or_modulo} + {expected_multiply_or_divide_or_modulo}) - {expected_multiply_or_divide_or_modulo})"); + + let shift = format!("{add_or_subtract} << {add_or_subtract} >> {add_or_subtract}"); + let expected_shift = format!("(({expected_add_or_subtract} << {expected_add_or_subtract}) >> {expected_add_or_subtract})"); + + let less_or_greater = format!("{shift} < {shift} > {shift} <= {shift} >= {shift}"); + let expected_less_or_greater = format!("(((({expected_shift} < {expected_shift}) > {expected_shift}) <= {expected_shift}) >= {expected_shift})"); + + let xor = format!("{less_or_greater} ^ {less_or_greater}"); + let expected_xor = format!("({expected_less_or_greater} ^ {expected_less_or_greater})"); + + let and = format!("{xor} & {xor}"); + let expected_and = format!("({expected_xor} & {expected_xor})"); + + let or = format!("{and} | {and}"); + let expected_or = format!("({expected_and} | {expected_and})"); + + let equal_or_not_equal = format!("{or} == {or} != {or}"); + let expected_equal_or_not_equal = + format!("(({expected_or} == {expected_or}) != {expected_or})"); + + let src = &equal_or_not_equal; + let expected_src = expected_equal_or_not_equal; + + let expr = parse_expression_no_errors(src); + let ExpressionKind::Infix(infix_expr) = expr.kind else { + panic!("Expected infix"); + }; + assert_eq!(infix_expr.to_string(), expected_src); + } + + #[test] + fn parses_empty_lambda() { + let src = "|| 1"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Lambda(lambda) = expr.kind else { + panic!("Expected lambda"); + }; + assert!(lambda.parameters.is_empty()); + assert_eq!(lambda.body.to_string(), "1"); + assert!(matches!(lambda.return_type.typ, UnresolvedTypeData::Unspecified)); + } + + #[test] + fn parses_lambda_with_arguments() { + let src = "|x, y: Field| 1"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Lambda(mut lambda) = expr.kind else { + panic!("Expected lambda"); + }; + assert_eq!(lambda.parameters.len(), 2); + + let (pattern, typ) = lambda.parameters.remove(0); + assert_eq!(pattern.to_string(), "x"); + assert!(matches!(typ.typ, UnresolvedTypeData::Unspecified)); + + let (pattern, typ) = lambda.parameters.remove(0); + assert_eq!(pattern.to_string(), "y"); + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parses_lambda_with_return_type() { + let src = "|| -> Field 1"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Lambda(lambda) = expr.kind else { + panic!("Expected lambda"); + }; + assert!(lambda.parameters.is_empty()); + assert_eq!(lambda.body.to_string(), "1"); + assert!(matches!(lambda.return_type.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parses_as_trait_path() { + let src = "::baz"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::AsTraitPath(as_trait_path) = expr.kind else { + panic!("Expected as_trait_path") + }; + assert_eq!(as_trait_path.typ.typ.to_string(), "Field"); + assert_eq!(as_trait_path.trait_path.to_string(), "foo::Bar"); + assert!(as_trait_path.trait_generics.is_empty()); + assert_eq!(as_trait_path.impl_item.to_string(), "baz"); + } + + #[test] + fn parses_comptime_expression() { + let src = "comptime { 1 }"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Comptime(block, _) = expr.kind else { + panic!("Expected comptime block"); + }; + assert_eq!(block.statements.len(), 1); + } + + #[test] + fn parses_type_path() { + let src = "Field::foo"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::TypePath(type_path) = expr.kind else { + panic!("Expected type_path"); + }; + assert_eq!(type_path.typ.to_string(), "Field"); + assert_eq!(type_path.item.to_string(), "foo"); + assert!(type_path.turbofish.is_empty()); + } + + #[test] + fn parses_type_path_with_generics() { + let src = "Field::foo::"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::TypePath(type_path) = expr.kind else { + panic!("Expected type_path"); + }; + assert_eq!(type_path.typ.to_string(), "Field"); + assert_eq!(type_path.item.to_string(), "foo"); + assert!(!type_path.turbofish.is_empty()); + } + + #[test] + fn parses_unquote_var() { + let src = "$foo::bar"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Unquote(expr) = expr.kind else { + panic!("Expected unquote"); + }; + let ExpressionKind::Variable(path) = expr.kind else { + panic!("Expected unquote"); + }; + assert_eq!(path.to_string(), "foo::bar"); + } + + #[test] + fn parses_unquote_expr() { + let src = "$(1 + 2)"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Unquote(expr) = expr.kind else { + panic!("Expected unquote"); + }; + assert_eq!(expr.kind.to_string(), "((1 + 2))"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 7b1f67a48bd..6d1fc611767 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -1,319 +1,483 @@ -use super::{ - attributes::{attributes, validate_attributes}, - block, fresh_statement, ident, keyword, maybe_comp_time, nothing, parameter_name_recovery, - parameter_recovery, parenthesized, parse_type, pattern, - primitives::token_kind, - self_parameter, - visibility::{item_visibility, visibility}, - where_clause, NoirParser, -}; -use crate::token::{Keyword, Token, TokenKind}; -use crate::{ - ast::{BlockExpression, IntegerBitSize}, - parser::spanned, +use crate::ast::{ + BlockExpression, GenericTypeArgs, Ident, Path, Pattern, UnresolvedTraitConstraint, + UnresolvedType, }; +use crate::token::{Attribute, Attributes, Keyword, Token}; +use crate::{ast::UnresolvedGenerics, parser::labels::ParsingRuleLabel}; use crate::{ ast::{ FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, UnresolvedTypeData, Visibility, }, - parser::{ParserError, ParserErrorReason}, -}; -use crate::{ - ast::{Signedness, UnresolvedGeneric, UnresolvedGenerics}, - parser::labels::ParsingRuleLabel, + parser::ParserErrorReason, }; +use acvm::AcirField; -use chumsky::prelude::*; use noirc_errors::Span; -/// function_definition: attribute function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block -/// function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block -pub(super) fn function_definition(allow_self: bool) -> impl NoirParser { - let body_or_error = - spanned(block(fresh_statement()).or_not()).validate(|(body, body_span), span, emit| { - if let Some(body) = body { - (body, body_span) - } else { - emit(ParserError::with_reason( - ParserErrorReason::ExpectedLeftBraceOrArrowAfterFunctionParameters, - span, - )); - (BlockExpression { statements: vec![] }, Span::from(span.end()..span.end())) - } - }); - - attributes() - .then(function_modifiers()) - .then_ignore(keyword(Keyword::Fn)) - .then(ident()) - .then(generics()) - .then( - parenthesized(function_parameters(allow_self)) - .then(function_return_type()) - .then(where_clause()) - .then(body_or_error) - // Allow parsing just `fn foo` for recovery and LSP autocompletion - .or_not(), +use super::parse_many::separated_by_comma_until_right_paren; +use super::pattern::SelfPattern; +use super::{pattern::PatternOrSelf, Parser}; + +pub(crate) struct FunctionDefinitionWithOptionalBody { + pub(crate) name: Ident, + pub(crate) generics: UnresolvedGenerics, + pub(crate) parameters: Vec, + pub(crate) body: Option, + pub(crate) span: Span, + pub(crate) where_clause: Vec, + pub(crate) return_type: FunctionReturnType, + pub(crate) return_visibility: Visibility, +} + +impl<'a> Parser<'a> { + /// Function = 'fn' identifier Generics FunctionParameters ( '->' Visibility Type )? WhereClause ( Block | ';' ) + pub(crate) fn parse_function( + &mut self, + attributes: Vec<(Attribute, Span)>, + visibility: ItemVisibility, + is_comptime: bool, + is_unconstrained: bool, + allow_self: bool, + ) -> NoirFunction { + self.parse_function_definition( + attributes, + visibility, + is_comptime, + is_unconstrained, + allow_self, ) - .validate(|args, span, emit| { - let ( - (((attributes, (is_unconstrained, visibility, is_comptime)), name), generics), - params_and_others, - ) = args; - - // Validate collected attributes, filtering them into function and secondary variants - let attributes = validate_attributes(attributes, span, emit); - - let function_definition = if let Some(params_and_others) = params_and_others { - let ( - ((parameters, (return_visibility, return_type)), where_clause), - (body, body_span), - ) = params_and_others; - - FunctionDefinition { - span: body_span, - name, - attributes, - is_unconstrained, - visibility, - is_comptime, - generics, - parameters, - body, - where_clause, - return_type, - return_visibility, - } + .into() + } + + pub(crate) fn parse_function_definition( + &mut self, + attributes: Vec<(Attribute, Span)>, + visibility: ItemVisibility, + is_comptime: bool, + is_unconstrained: bool, + allow_self: bool, + ) -> FunctionDefinition { + let attributes = self.validate_attributes(attributes); + + let func = self.parse_function_definition_with_optional_body( + false, // allow optional body + allow_self, + ); + + FunctionDefinition { + name: func.name, + attributes, + is_unconstrained, + is_comptime, + visibility, + generics: func.generics, + parameters: func.parameters, + body: func.body.unwrap_or_else(empty_body), + span: func.span, + where_clause: func.where_clause, + return_type: func.return_type, + return_visibility: func.return_visibility, + } + } + + pub(super) fn parse_function_definition_with_optional_body( + &mut self, + allow_optional_body: bool, + allow_self: bool, + ) -> FunctionDefinitionWithOptionalBody { + let Some(name) = self.eat_ident() else { + self.expected_identifier(); + return empty_function(self.previous_token_span); + }; + + let generics = self.parse_generics(); + let parameters = self.parse_function_parameters(allow_self); + + let (return_type, return_visibility) = if self.eat(Token::Arrow) { + let visibility = self.parse_visibility(); + (FunctionReturnType::Ty(self.parse_type_or_error()), visibility) + } else { + (FunctionReturnType::Default(self.span_at_previous_token_end()), Visibility::Private) + }; + + let where_clause = self.parse_where_clause(); + + let body_start_span = self.current_token_span; + let body = if self.eat_semicolons() { + if !allow_optional_body { + self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_span); + } + + None + } else { + Some(self.parse_block().unwrap_or_else(empty_body)) + }; + + FunctionDefinitionWithOptionalBody { + name, + generics, + parameters, + body, + span: self.span_since(body_start_span), + where_clause, + return_type, + return_visibility, + } + } + + /// FunctionParameters = '(' FunctionParametersList? ')' + /// + /// FunctionParametersList = FunctionParameter ( ',' FunctionParameter )* ','? + /// + /// FunctionParameter = Visibility PatternOrSelf ':' Type + fn parse_function_parameters(&mut self, allow_self: bool) -> Vec { + if !self.eat_left_paren() { + return Vec::new(); + } + + self.parse_many("parameters", separated_by_comma_until_right_paren(), |parser| { + parser.parse_function_parameter(allow_self) + }) + } + + fn parse_function_parameter(&mut self, allow_self: bool) -> Option { + loop { + let start_span = self.current_token_span; + + let pattern_or_self = if allow_self { + self.parse_pattern_or_self() } else { - emit(ParserError::with_reason( - ParserErrorReason::ExpectedLeftParenOrLeftBracketAfterFunctionName, - span, - )); - - let empty_span = Span::from(span.end()..span.end()); - FunctionDefinition { - span: empty_span, - name, - attributes, - is_unconstrained, - visibility, - is_comptime, - generics, - parameters: Vec::new(), - body: BlockExpression { statements: vec![] }, - where_clause: Vec::new(), - return_type: FunctionReturnType::Default(empty_span), - return_visibility: Visibility::Private, + self.parse_pattern().map(PatternOrSelf::Pattern) + }; + + let Some(pattern_or_self) = pattern_or_self else { + self.expected_label(ParsingRuleLabel::Pattern); + + // Let's try with the next token + self.bump(); + if self.at_eof() { + return None; + } else { + continue; } }; - function_definition.into() - }) -} -/// function_modifiers: 'unconstrained'? (visibility)? -/// -/// returns (is_unconstrained, visibility) for whether each keyword was present -pub(super) fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { - keyword(Keyword::Unconstrained).or_not().then(item_visibility()).then(maybe_comp_time()).map( - |((unconstrained, visibility), comptime)| (unconstrained.is_some(), visibility, comptime), - ) -} + return Some(match pattern_or_self { + PatternOrSelf::Pattern(pattern) => self.pattern_param(pattern, start_span), + PatternOrSelf::SelfPattern(self_pattern) => self.self_pattern_param(self_pattern), + }); + } + } -pub(super) fn numeric_generic() -> impl NoirParser { - keyword(Keyword::Let) - .ignore_then(ident()) - .then_ignore(just(Token::Colon)) - .then(parse_type()) - .map(|(ident, typ)| UnresolvedGeneric::Numeric { ident, typ }) - .validate(|generic, span, emit| { - if let UnresolvedGeneric::Numeric { typ, .. } = &generic { - if let UnresolvedTypeData::Integer(signedness, bit_size) = typ.typ { - if matches!(signedness, Signedness::Signed) - || matches!(bit_size, IntegerBitSize::SixtyFour) - { - emit(ParserError::with_reason( - ParserErrorReason::ForbiddenNumericGenericType, - span, - )); - } + fn pattern_param(&mut self, pattern: Pattern, start_span: Span) -> Param { + let (visibility, typ) = if !self.eat_colon() { + self.push_error( + ParserErrorReason::MissingTypeForFunctionParameter, + Span::from(pattern.span().start()..self.current_token_span.end()), + ); + + let visibility = Visibility::Private; + let typ = UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }; + (visibility, typ) + } else { + (self.parse_visibility(), self.parse_type_or_error()) + }; + + Param { visibility, pattern, typ, span: self.span_since(start_span) } + } + + fn self_pattern_param(&mut self, self_pattern: SelfPattern) -> Param { + let ident_span = self.previous_token_span; + let ident = Ident::new("self".to_string(), ident_span); + let path = Path::from_single("Self".to_owned(), ident_span); + let no_args = GenericTypeArgs::default(); + let mut self_type = UnresolvedTypeData::Named(path, no_args, true).with_span(ident_span); + let mut pattern = Pattern::Identifier(ident); + + if self_pattern.reference { + self_type = + UnresolvedTypeData::MutableReference(Box::new(self_type)).with_span(ident_span); + } else if self_pattern.mutable { + pattern = Pattern::Mutable(Box::new(pattern), ident_span, true); + } + + Param { + visibility: Visibility::Private, + pattern, + typ: self_type, + span: self.span_since(ident_span), + } + } + + /// Visibility + /// = 'pub' + /// | 'return_data' + /// | 'call_data' '(' int ')' + /// | nothing + fn parse_visibility(&mut self) -> Visibility { + if self.eat_keyword(Keyword::Pub) { + return Visibility::Public; + } + + if self.eat_keyword(Keyword::ReturnData) { + return Visibility::ReturnData; + } + + if self.eat_keyword(Keyword::CallData) { + if self.eat_left_paren() { + if let Some(int) = self.eat_int() { + self.eat_or_error(Token::RightParen); + + let id = int.to_u128() as u32; + return Visibility::CallData(id); + } else { + self.expected_label(ParsingRuleLabel::Integer); + self.eat_right_paren(); + return Visibility::CallData(0); } + } else { + self.expected_token(Token::LeftParen); + return Visibility::CallData(0); } - generic - }) -} + } -pub(super) fn generic_type() -> impl NoirParser { - ident().map(UnresolvedGeneric::Variable) -} + Visibility::Private + } + + fn validate_attributes(&mut self, attributes: Vec<(Attribute, Span)>) -> Attributes { + let mut primary = None; + let mut secondary = Vec::new(); + + for (attribute, span) in attributes { + match attribute { + Attribute::Function(attr) => { + if primary.is_some() { + self.push_error(ParserErrorReason::MultipleFunctionAttributesFound, span); + } + primary = Some(attr); + } + Attribute::Secondary(attr) => secondary.push(attr), + } + } -pub(super) fn resolved_generic() -> impl NoirParser { - token_kind(TokenKind::QuotedType).map_with_span(|token, span| match token { - Token::QuotedType(id) => UnresolvedGeneric::Resolved(id, span), - _ => unreachable!("token_kind(QuotedType) guarantees we parse a quoted type"), - }) + Attributes { function: primary, secondary } + } } -pub(super) fn generic() -> impl NoirParser { - generic_type().or(numeric_generic()).or(resolved_generic()) +fn empty_function(span: Span) -> FunctionDefinitionWithOptionalBody { + FunctionDefinitionWithOptionalBody { + name: Ident::default(), + generics: Vec::new(), + parameters: Vec::new(), + body: None, + span: Span::from(span.end()..span.end()), + where_clause: Vec::new(), + return_type: FunctionReturnType::Default(Span::default()), + return_visibility: Visibility::Private, + } } -/// non_empty_ident_list: ident ',' non_empty_ident_list -/// | ident -/// -/// generics: '<' non_empty_ident_list '>' -/// | %empty -pub(super) fn generics() -> impl NoirParser { - generic() - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not() - .map(|opt| opt.unwrap_or_default()) +fn empty_body() -> BlockExpression { + BlockExpression { statements: Vec::new() } } -pub(super) fn function_return_type() -> impl NoirParser<(Visibility, FunctionReturnType)> { - #[allow(deprecated)] - just(Token::Arrow).ignore_then(visibility()).then(spanned(parse_type())).or_not().map_with_span( - |ret, span| match ret { - Some((visibility, (ty, _))) => (visibility, FunctionReturnType::Ty(ty)), - None => (Visibility::Private, FunctionReturnType::Default(span)), +#[cfg(test)] +mod tests { + use crate::{ + ast::{NoirFunction, UnresolvedTypeData, Visibility}, + parser::{ + parser::{ + parse_program, + tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, + }, + ItemKind, ParserErrorReason, }, - ) -} + }; -fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { - let typ = parse_type().recover_via(parameter_recovery()); + fn parse_function_no_error(src: &str) -> NoirFunction { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(noir_function) = item.kind else { + panic!("Expected function"); + }; + noir_function + } - let full_parameter = pattern() - .recover_via(parameter_name_recovery()) - .then_ignore(just(Token::Colon)) - .then(visibility()) - .then(typ) - .map_with_span(|((pattern, visibility), typ), span| Param { - visibility, - pattern, - typ, - span, - }); + #[test] + fn parse_simple_function() { + let src = "fn foo() {}"; + let noir_function = parse_function_no_error(src); + assert_eq!("foo", noir_function.def.name.to_string()); + assert!(noir_function.def.parameters.is_empty()); + assert!(noir_function.def.generics.is_empty()); + } - let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; + #[test] + fn parse_function_with_generics() { + let src = "fn foo() {}"; + let noir_function = parse_function_no_error(src); + assert_eq!(noir_function.def.generics.len(), 1); + } - let parameter = full_parameter.or(self_parameter); + #[test] + fn parse_function_with_arguments() { + let src = "fn foo(x: Field, y: Field) {}"; + let mut noir_function = parse_function_no_error(src); + assert_eq!(noir_function.def.parameters.len(), 2); - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} + let param = noir_function.def.parameters.remove(0); + assert_eq!("x", param.pattern.to_string()); + assert_eq!("Field", param.typ.to_string()); + assert_eq!(param.visibility, Visibility::Private); -#[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; + let param = noir_function.def.parameters.remove(0); + assert_eq!("y", param.pattern.to_string()); + assert_eq!("Field", param.typ.to_string()); + assert_eq!(param.visibility, Visibility::Private); + } #[test] - fn regression_skip_comment() { - parse_all( - function_definition(false), - vec![ - "fn main( - // This comment should be skipped - x : Field, - // And this one - y : Field, - ) { - }", - "fn main(x : Field, y : Field,) { - foo::bar( - // Comment for x argument - x, - // Comment for y argument - y - ) - }", - ], - ); + fn parse_function_with_argument_pub_visibility() { + let src = "fn foo(x: pub Field) {}"; + let mut noir_function = parse_function_no_error(src); + assert_eq!(noir_function.def.parameters.len(), 1); + + let param = noir_function.def.parameters.remove(0); + assert_eq!("x", param.pattern.to_string()); + assert_eq!("Field", param.typ.to_string()); + assert_eq!(param.visibility, Visibility::Public); } #[test] - fn parse_function() { - parse_all( - function_definition(false), - vec![ - "fn func_name() {}", - "fn f(foo: pub u8, y : pub Field) -> u8 { x + a }", - "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) {}", - "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", - "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", - "fn main(x: pub u8, y: pub u8) -> pub [u8; 2] { [x, y] }", - "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", - "fn func_name(f: Field, y : T) where T: SomeTrait {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 + TraitY {}", - "fn func_name(f: Field, y : T, z : U) where SomeStruct: SomeTrait {}", - // 'where u32: SomeTrait' is allowed in Rust. - // It will result in compiler error in case SomeTrait isn't implemented for u32. - "fn func_name(f: Field, y : T) where u32: SomeTrait {}", - // A trailing plus is allowed by Rust, so we support it as well. - "fn func_name(f: Field, y : T) where T: SomeTrait + {}", - // The following should produce compile error on later stage. From the parser's perspective it's fine - "fn func_name(f: Field, y : Field, z : Field) where T: SomeTrait {}", - // TODO: this fails with known EOF != EOF error - // https://github.com/noir-lang/noir/issues/4763 - // fn func_name(x: impl Eq) {} with error Expected an end of input but found end of input - // "fn func_name(x: impl Eq) {}", - "fn func_name(x: impl Eq, y : T) where T: SomeTrait + Eq {}", - "fn func_name(x: [Field; N]) {}", - ], - ); + fn parse_function_with_argument_return_data_visibility() { + let src = "fn foo(x: return_data Field) {}"; + let mut noir_function = parse_function_no_error(src); + assert_eq!(noir_function.def.parameters.len(), 1); - parse_all_failing( - function_definition(false), - vec![ - "fn x2( f: []Field,,) {}", - "fn ( f: []Field) {}", - "fn ( f: []Field) {}", - // TODO: Check for more specific error messages - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: {}", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where SomeTrait {}", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) SomeTrait {}", - // A leading plus is not allowed. - "fn func_name(f: Field, y : T) where T: + SomeTrait {}", - "fn func_name(f: Field, y : T) where T: TraitX + {}", - // Test ill-formed numeric generics - "fn func_name(y: T) {}", - "fn func_name(y: T) {}", - "fn func_name(y: T) {}", - // Test failure of missing `let` - "fn func_name(y: T) {}", - // Test that signed numeric generics are banned - "fn func_name() {}", - // Test that `u64` is banned - "fn func_name(x: [Field; N]) {}", - ], - ); + let param = noir_function.def.parameters.remove(0); + assert_eq!(param.visibility, Visibility::ReturnData); + } + + #[test] + fn parse_function_with_argument_call_data_visibility() { + let src = "fn foo(x: call_data(42) Field) {}"; + let mut noir_function = parse_function_no_error(src); + assert_eq!(noir_function.def.parameters.len(), 1); + + let param = noir_function.def.parameters.remove(0); + assert_eq!(param.visibility, Visibility::CallData(42)); + } + + #[test] + fn parse_function_return_type() { + let src = "fn foo() -> Field {}"; + let noir_function = parse_function_no_error(src); + assert_eq!(noir_function.def.return_visibility, Visibility::Private); + assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); } #[test] - fn parse_recover_function_without_body() { - let src = "fn foo(x: i32)"; + fn parse_function_return_visibility() { + let src = "fn foo() -> pub Field {}"; + let noir_function = parse_function_no_error(src); + assert_eq!(noir_function.def.return_visibility, Visibility::Public); + assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); + } - let (noir_function, errors) = parse_recover(function_definition(false), src); + #[test] + fn parse_function_unclosed_parentheses() { + let src = "fn foo(x: i32,"; + let (module, errors) = parse_program(src); assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected { or -> after function parameters"); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Function(noir_function) = &item.kind else { + panic!("Expected function"); + }; + assert_eq!("foo", noir_function.def.name.to_string()); + } + + #[test] + fn parse_error_multiple_function_attributes_found() { + let src = " + #[foreign(foo)] #[oracle(bar)] fn foo() {} + ^^^^^^^^^^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (_, errors) = parse_program(&src); + let reason = get_single_error_reason(&errors, span); + assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); + } - let noir_function = noir_function.unwrap(); - assert_eq!(noir_function.name(), "foo"); + #[test] + fn parse_function_found_semicolon_instead_of_braces() { + let src = " + fn foo(); + ^ + "; + let (src, span) = get_source_with_error_span(src); + let (_, errors) = parse_program(&src); + let reason = get_single_error_reason(&errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedFunctionBody)); + } + + #[test] + fn recovers_on_wrong_parameter_name() { + let src = " + fn foo(1 x: i32) {} + ^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + assert_eq!(module.items.len(), 1); + let ItemKind::Function(noir_function) = &module.items[0].kind else { + panic!("Expected function"); + }; assert_eq!(noir_function.parameters().len(), 1); - assert!(noir_function.def.body.statements.is_empty()); + + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a pattern but found 1"); + } + + #[test] + fn recovers_on_missing_colon_after_parameter_name() { + let src = " + fn foo(x, y: i32) {} + ^^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + assert_eq!(module.items.len(), 1); + let ItemKind::Function(noir_function) = &module.items[0].kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.parameters().len(), 2); + + let error = get_single_error(&errors, span); + assert!(error.to_string().contains("Missing type for function parameter")); + } + + #[test] + fn recovers_on_missing_type_after_parameter_colon() { + let src = " + fn foo(x: , y: i32) {} + ^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + assert_eq!(module.items.len(), 1); + let ItemKind::Function(noir_function) = &module.items[0].kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.parameters().len(), 2); + + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a type but found ,"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs new file mode 100644 index 00000000000..2c8ba5a2a65 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -0,0 +1,283 @@ +use crate::{ + ast::{ + GenericTypeArg, GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, + }, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, + token::{Keyword, Token, TokenKind}, +}; + +use super::{parse_many::separated_by_comma, Parser}; + +impl<'a> Parser<'a> { + /// Generics = ( '<' GenericsList? '>' )? + /// + /// GenericsList = Generic ( ',' Generic )* ','? + pub(super) fn parse_generics(&mut self) -> UnresolvedGenerics { + if !self.eat_less() { + return Vec::new(); + } + + self.parse_many( + "generic parameters", + separated_by_comma().until(Token::Greater), + Self::parse_generic_in_list, + ) + } + + fn parse_generic_in_list(&mut self) -> Option { + if let Some(generic) = self.parse_generic() { + Some(generic) + } else { + self.expected_label(ParsingRuleLabel::GenericParameter); + None + } + } + + /// Generic + /// = VariableGeneric + /// | NumericGeneric + /// | ResolvedGeneric + fn parse_generic(&mut self) -> Option { + if let Some(generic) = self.parse_variable_generic() { + return Some(generic); + } + + if let Some(generic) = self.parse_numeric_generic() { + return Some(generic); + } + + if let Some(generic) = self.parse_resolved_generic() { + return Some(generic); + } + + None + } + + /// VariableGeneric = identifier + fn parse_variable_generic(&mut self) -> Option { + self.eat_ident().map(UnresolvedGeneric::Variable) + } + + /// NumericGeneric = 'let' identifier ':' Type + fn parse_numeric_generic(&mut self) -> Option { + if !self.eat_keyword(Keyword::Let) { + return None; + } + + let ident = self.eat_ident()?; + + if !self.eat_colon() { + // If we didn't get a type after the colon, error and assume it's u32 + self.push_error( + ParserErrorReason::MissingTypeForNumericGeneric, + self.current_token_span, + ); + let typ = UnresolvedType { + typ: UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), + span: self.span_at_previous_token_end(), + }; + return Some(UnresolvedGeneric::Numeric { ident, typ }); + } + + let typ = self.parse_type_or_error(); + if let UnresolvedTypeData::Integer(signedness, bit_size) = &typ.typ { + if matches!(signedness, Signedness::Signed) + || matches!(bit_size, IntegerBitSize::SixtyFour) + { + self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); + } + } + + Some(UnresolvedGeneric::Numeric { ident, typ }) + } + + /// ResolvedGeneric = quoted_type + fn parse_resolved_generic(&mut self) -> Option { + let token = self.eat_kind(TokenKind::QuotedType)?; + match token.into_token() { + Token::QuotedType(id) => { + Some(UnresolvedGeneric::Resolved(id, self.previous_token_span)) + } + _ => unreachable!(), + } + } + + /// GenericTypeArgs = ( '<' GenericTypeArgsList? '>' ) + /// + /// GenericTypeArgsList = GenericTypeArg ( ',' GenericTypeArg )* ','? + /// + /// GenericTypeArg + /// = NamedTypeArg + /// | OrderedTypeArg + /// + /// NamedTypeArg = identifier '=' Type + /// + /// OrderedTypeArg = TypeOrTypeExpression + pub(super) fn parse_generic_type_args(&mut self) -> GenericTypeArgs { + let mut generic_type_args = GenericTypeArgs::default(); + if !self.eat_less() { + return generic_type_args; + } + + let generics = self.parse_many( + "generic parameters", + separated_by_comma().until(Token::Greater), + Self::parse_generic_type_arg, + ); + + for generic in generics { + match generic { + GenericTypeArg::Ordered(typ) => { + generic_type_args.ordered_args.push(typ); + } + GenericTypeArg::Named(name, typ) => { + generic_type_args.named_args.push((name, typ)); + } + } + } + + generic_type_args + } + + fn parse_generic_type_arg(&mut self) -> Option { + if matches!(self.token.token(), Token::Ident(..)) && self.next_is(Token::Assign) { + let ident = self.eat_ident().unwrap(); + + self.eat_assign(); + + let typ = self.parse_type_or_error(); + return Some(GenericTypeArg::Named(ident, typ)); + } + + // Otherwise + let Some(typ) = self.parse_type_or_type_expression() else { + self.expected_label(ParsingRuleLabel::TypeOrTypeExpression); + return None; + }; + + Some(GenericTypeArg::Ordered(typ)) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + }; + + fn parse_generics_no_errors(src: &str) -> Vec { + let mut parser = Parser::for_str(src); + let generics = parser.parse_generics(); + expect_no_errors(&parser.errors); + generics + } + + fn parse_generic_type_args_no_errors(src: &str) -> GenericTypeArgs { + let mut parser = Parser::for_str(src); + let generics = parser.parse_generic_type_args(); + expect_no_errors(&parser.errors); + generics + } + + #[test] + fn parses_no_generics() { + let src = "1"; + let generics = parse_generics_no_errors(src); + assert!(generics.is_empty()); + } + + #[test] + fn parses_generics() { + let src = ""; + let mut generics = parse_generics_no_errors(src); + assert_eq!(generics.len(), 2); + + let generic = generics.remove(0); + let UnresolvedGeneric::Variable(ident) = generic else { + panic!("Expected generic variable"); + }; + assert_eq!("A", ident.to_string()); + + let generic = generics.remove(0); + let UnresolvedGeneric::Numeric { ident, typ } = generic else { + panic!("Expected generic numeric"); + }; + assert_eq!("B", ident.to_string()); + assert_eq!( + typ.typ, + UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) + ); + } + + #[test] + fn parses_no_generic_type_args() { + let src = "1"; + let generics = parse_generic_type_args_no_errors(src); + assert!(generics.is_empty()); + } + + #[test] + fn parses_generic_type_args() { + let src = ""; + let generics = parse_generic_type_args_no_errors(src); + assert!(!generics.is_empty()); + assert_eq!(generics.ordered_args.len(), 1); + assert_eq!(generics.ordered_args[0].to_string(), "i32"); + assert_eq!(generics.named_args.len(), 1); + assert_eq!(generics.named_args[0].0.to_string(), "X"); + assert_eq!(generics.named_args[0].1.to_string(), "Field"); + } + + #[test] + fn parses_generic_type_arg_that_is_a_path() { + let src = ""; + let generics = parse_generic_type_args_no_errors(src); + assert!(!generics.is_empty()); + assert_eq!(generics.ordered_args.len(), 1); + assert_eq!(generics.ordered_args[0].to_string(), "foo::Bar"); + assert_eq!(generics.named_args.len(), 0); + } + + #[test] + fn parses_generic_type_arg_that_is_an_int() { + let src = "<1>"; + let generics = parse_generic_type_args_no_errors(src); + assert!(!generics.is_empty()); + assert_eq!(generics.ordered_args.len(), 1); + assert_eq!(generics.ordered_args[0].to_string(), "1"); + } + + #[test] + fn parse_numeric_generic_error_if_invalid_integer() { + let src = " + + ^^^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + parser.parse_generics(); + let reason = get_single_error_reason(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); + } + + #[test] + fn parse_arithmetic_generic_on_variable() { + let src = ""; + let generics = parse_generic_type_args_no_errors(src); + assert_eq!(generics.ordered_args[0].to_string(), "(N - 1)"); + } + + #[test] + fn parse_var_with_turbofish_in_generic() { + let src = ">"; + let generics = parse_generic_type_args_no_errors(src); + assert_eq!(generics.ordered_args[0].to_string(), "N<1>"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs new file mode 100644 index 00000000000..2ea6457dc0b --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -0,0 +1,168 @@ +use noirc_errors::Span; + +use crate::{ + ast::{ + Expression, ExpressionKind, Ident, LetStatement, Pattern, UnresolvedType, + UnresolvedTypeData, + }, + parser::ParserErrorReason, + token::{Attribute, Token}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + /// Global = 'global' identifier OptionalTypeAnnotation '=' Expression ';' + pub(crate) fn parse_global( + &mut self, + attributes: Vec<(Attribute, Span)>, + comptime: bool, + mutable: bool, + ) -> LetStatement { + // Only comptime globals are allowed to be mutable, but we always parse the `mut` + // and throw the error in name resolution. + + let attributes = self.validate_secondary_attributes(attributes); + + let Some(ident) = self.eat_ident() else { + self.eat_semicolons(); + return LetStatement { + pattern: ident_to_pattern(Ident::default(), mutable), + r#type: UnresolvedType { + typ: UnresolvedTypeData::Unspecified, + span: Span::default(), + }, + expression: Expression { kind: ExpressionKind::Error, span: Span::default() }, + attributes, + comptime, + }; + }; + + let pattern = ident_to_pattern(ident, mutable); + + let typ = self.parse_optional_type_annotation(); + + let expression = if self.eat_assign() { + self.parse_expression_or_error() + } else { + self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.span()); + Expression { kind: ExpressionKind::Error, span: Span::default() } + }; + + if !self.eat_semicolons() { + self.expected_token(Token::Semicolon); + } + + LetStatement { pattern, r#type: typ, expression, attributes, comptime } + } +} + +fn ident_to_pattern(ident: Ident, mutable: bool) -> Pattern { + if mutable { + Pattern::Mutable(Box::new(Pattern::Identifier(ident)), Span::default(), false) + } else { + Pattern::Identifier(ident) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{ + IntegerBitSize, ItemVisibility, LetStatement, Pattern, Signedness, UnresolvedTypeData, + }, + parser::{ + parser::{ + parse_program, + tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, + }, + ItemKind, ParserErrorReason, + }, + }; + + fn parse_global_no_errors(src: &str) -> (LetStatement, ItemVisibility) { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Global(let_statement, visibility) = item.kind else { + panic!("Expected global"); + }; + (let_statement, visibility) + } + + #[test] + fn parse_global_no_type_annotation() { + let src = "global foo = 1;"; + let (let_statement, visibility) = parse_global_no_errors(src); + let Pattern::Identifier(name) = &let_statement.pattern else { + panic!("Expected identifier pattern"); + }; + assert_eq!("foo", name.to_string()); + assert!(matches!(let_statement.r#type.typ, UnresolvedTypeData::Unspecified)); + assert!(!let_statement.comptime); + assert_eq!(visibility, ItemVisibility::Private); + } + + #[test] + fn parse_global_with_type_annotation() { + let src = "global foo: i32 = 1;"; + let (let_statement, _visibility) = parse_global_no_errors(src); + let Pattern::Identifier(name) = &let_statement.pattern else { + panic!("Expected identifier pattern"); + }; + assert_eq!("foo", name.to_string()); + assert!(matches!( + let_statement.r#type.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) + )); + } + + #[test] + fn parse_comptime_global() { + let src = "comptime global foo: i32 = 1;"; + let (let_statement, _visibility) = parse_global_no_errors(src); + assert!(let_statement.comptime); + } + + #[test] + fn parse_mutable_global() { + let src = "mut global foo: i32 = 1;"; + let (let_statement, _visibility) = parse_global_no_errors(src); + let Pattern::Mutable(pattern, _, _) = &let_statement.pattern else { + panic!("Expected mutable pattern"); + }; + let pattern: &Pattern = pattern; + let Pattern::Identifier(name) = pattern else { + panic!("Expected identifier pattern"); + }; + assert_eq!("foo", name.to_string()); + } + + #[test] + fn parse_global_no_value() { + let src = " + global foo; + ^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (_, errors) = parse_program(&src); + let reason = get_single_error_reason(&errors, span); + assert!(matches!(reason, ParserErrorReason::GlobalWithoutValue)); + } + + #[test] + fn parse_global_no_semicolon() { + let src = " + global foo = 1 + ^ + "; + let (src, span) = get_source_with_error_span(src); + let (_, errors) = parse_program(&src); + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a ; but found end of input"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs new file mode 100644 index 00000000000..460c7bded15 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -0,0 +1,567 @@ +use noirc_errors::Span; + +use crate::{ + ast::{ + Documented, Expression, ExpressionKind, GenericTypeArgs, Ident, ItemVisibility, + NoirFunction, NoirTraitImpl, Path, TraitImplItem, TraitImplItemKind, TypeImpl, + UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, + }, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, + token::{Keyword, Token, TokenKind}, +}; + +use super::{parse_many::without_separator, Parser}; + +pub(crate) enum Impl { + Impl(TypeImpl), + TraitImpl(NoirTraitImpl), +} + +impl<'a> Parser<'a> { + /// Impl + /// = TypeImpl + /// | TraitImpl + pub(crate) fn parse_impl(&mut self) -> Impl { + let generics = self.parse_generics(); + + let type_span_start = self.current_token_span; + let object_type = self.parse_type_or_error(); + let type_span = self.span_since(type_span_start); + + if self.eat_keyword(Keyword::For) { + if let UnresolvedTypeData::Named(trait_name, trait_generics, _) = object_type.typ { + return Impl::TraitImpl(self.parse_trait_impl( + generics, + trait_generics, + trait_name, + )); + } else { + self.push_error( + ParserErrorReason::ExpectedTrait { found: object_type.typ.to_string() }, + self.current_token_span, + ); + + // Error, but we continue parsing the type and assume this is going to be a regular type impl + self.parse_type(); + }; + } + + self.parse_type_impl(object_type, type_span, generics) + } + + /// TypeImpl = 'impl' Generics Type TypeImplBody + fn parse_type_impl( + &mut self, + object_type: UnresolvedType, + type_span: Span, + generics: Vec, + ) -> Impl { + let where_clause = self.parse_where_clause(); + let methods = self.parse_type_impl_body(); + + Impl::Impl(TypeImpl { object_type, type_span, generics, where_clause, methods }) + } + + /// TypeImplBody = '{' TypeImplItem* '}' + /// + /// TypeImplItem = OuterDocComments Attributes Modifiers Function + fn parse_type_impl_body(&mut self) -> Vec<(Documented, Span)> { + if !self.eat_left_brace() { + self.expected_token(Token::LeftBrace); + return Vec::new(); + } + + self.parse_many( + "type impl methods", + without_separator().until(Token::RightBrace), + Self::parse_type_impl_method, + ) + } + + fn parse_type_impl_method(&mut self) -> Option<(Documented, Span)> { + self.parse_item_in_list( + ParsingRuleLabel::TokenKind(TokenKind::Token(Token::Keyword(Keyword::Fn))), + |parser| { + let doc_comments = parser.parse_outer_doc_comments(); + let start_span = parser.current_token_span; + let attributes = parser.parse_attributes(); + let modifiers = parser.parse_modifiers( + false, // allow mutable + ); + + if parser.eat_keyword(Keyword::Fn) { + let method = parser.parse_function( + attributes, + modifiers.visibility, + modifiers.comptime.is_some(), + modifiers.unconstrained.is_some(), + true, // allow_self + ); + Some((Documented::new(method, doc_comments), parser.span_since(start_span))) + } else { + parser.modifiers_not_followed_by_an_item(modifiers); + None + } + }, + ) + } + + /// TraitImpl = 'impl' Generics Path GenericTypeArgs 'for' Type TraitImplBody + fn parse_trait_impl( + &mut self, + impl_generics: Vec, + trait_generics: GenericTypeArgs, + trait_name: Path, + ) -> NoirTraitImpl { + let object_type = self.parse_type_or_error(); + let where_clause = self.parse_where_clause(); + let items = self.parse_trait_impl_body(); + + NoirTraitImpl { + impl_generics, + trait_name, + trait_generics, + object_type, + where_clause, + items, + } + } + + /// TraitImplBody = '{' TraitImplItem* '}' + fn parse_trait_impl_body(&mut self) -> Vec> { + if !self.eat_left_brace() { + self.expected_token(Token::LeftBrace); + return Vec::new(); + } + + self.parse_many( + "trait impl item", + without_separator().until(Token::RightBrace), + Self::parse_trait_impl_item, + ) + } + + fn parse_trait_impl_item(&mut self) -> Option> { + self.parse_item_in_list(ParsingRuleLabel::TraitImplItem, |parser| { + let start_span = parser.current_token_span; + let doc_comments = parser.parse_outer_doc_comments(); + + if let Some(kind) = parser.parse_trait_impl_item_kind() { + let item = TraitImplItem { kind, span: parser.span_since(start_span) }; + Some(Documented::new(item, doc_comments)) + } else { + None + } + }) + } + + /// TraitImplItem + /// = TraitImplType + /// | TraitImplConstant + /// | TraitImplFunction + fn parse_trait_impl_item_kind(&mut self) -> Option { + if let Some(kind) = self.parse_trait_impl_type() { + return Some(kind); + } + + if let Some(kind) = self.parse_trait_impl_constant() { + return Some(kind); + } + + self.parse_trait_impl_function() + } + + /// TraitImplType = 'type' identifier ( ':' Type )? ';' + fn parse_trait_impl_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::Type) { + return None; + } + + let Some(name) = self.eat_ident() else { + self.expected_identifier(); + self.eat_semicolons(); + return Some(TraitImplItemKind::Type { + name: Ident::default(), + alias: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + }); + }; + + let alias = if self.eat_assign() { + self.parse_type_or_error() + } else { + UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() } + }; + + self.eat_semicolons(); + + Some(TraitImplItemKind::Type { name, alias }) + } + + /// TraitImplConstant = 'let' identifier OptionalTypeAnnotation ';' + fn parse_trait_impl_constant(&mut self) -> Option { + if !self.eat_keyword(Keyword::Let) { + return None; + } + + let name = match self.eat_ident() { + Some(name) => name, + None => { + self.expected_identifier(); + Ident::default() + } + }; + + let typ = self.parse_optional_type_annotation(); + + let expr = if self.eat_assign() { + self.parse_expression_or_error() + } else { + self.expected_token(Token::Assign); + Expression { kind: ExpressionKind::Error, span: Span::default() } + }; + + self.eat_semicolons(); + + Some(TraitImplItemKind::Constant(name, typ, expr)) + } + + /// TraitImplFunction = Attributes Modifiers Function + fn parse_trait_impl_function(&mut self) -> Option { + let attributes = self.parse_attributes(); + + let modifiers = self.parse_modifiers( + false, // allow mut + ); + if modifiers.visibility != ItemVisibility::Private { + self.push_error( + ParserErrorReason::TraitImplVisibilityIgnored, + modifiers.visibility_span, + ); + } + + if !self.eat_keyword(Keyword::Fn) { + self.modifiers_not_followed_by_an_item(modifiers); + return None; + } + + let noir_function = self.parse_function( + attributes, + ItemVisibility::Public, + modifiers.comptime.is_some(), + modifiers.unconstrained.is_some(), + true, // allow_self + ); + Some(TraitImplItemKind::Function(noir_function)) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{ + ItemVisibility, NoirTraitImpl, Pattern, TraitImplItemKind, TypeImpl, UnresolvedTypeData, + }, + parser::{ + parser::{ + parse_program, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + }, + ItemKind, + }, + }; + + fn parse_type_impl_no_errors(src: &str) -> TypeImpl { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Impl(type_impl) = item.kind else { + panic!("Expected type impl"); + }; + type_impl + } + + fn parse_trait_impl_no_errors(src: &str) -> NoirTraitImpl { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::TraitImpl(noir_trait_impl) = item.kind else { + panic!("Expected trait impl"); + }; + noir_trait_impl + } + + #[test] + fn parse_empty_impl() { + let src = "impl Foo {}"; + let type_impl = parse_type_impl_no_errors(src); + assert_eq!(type_impl.object_type.to_string(), "Foo"); + assert!(type_impl.generics.is_empty()); + assert!(type_impl.methods.is_empty()); + } + + #[test] + fn parse_empty_impl_with_generics() { + let src = "impl Foo {}"; + let type_impl = parse_type_impl_no_errors(src); + assert_eq!(type_impl.object_type.to_string(), "Foo"); + assert_eq!(type_impl.generics.len(), 2); + assert!(type_impl.methods.is_empty()); + } + + #[test] + fn parse_impl_with_methods() { + let src = "impl Foo { unconstrained fn foo() {} pub comptime fn bar() {} }"; + let mut type_impl = parse_type_impl_no_errors(src); + assert_eq!(type_impl.object_type.to_string(), "Foo"); + assert_eq!(type_impl.methods.len(), 2); + + let (method, _) = type_impl.methods.remove(0); + let method = method.item; + assert_eq!(method.def.name.to_string(), "foo"); + assert!(method.def.is_unconstrained); + assert!(!method.def.is_comptime); + assert_eq!(method.def.visibility, ItemVisibility::Private); + + let (method, _) = type_impl.methods.remove(0); + let method = method.item; + assert_eq!(method.def.name.to_string(), "bar"); + assert!(method.def.is_comptime); + assert_eq!(method.def.visibility, ItemVisibility::Public); + } + + #[test] + fn parse_impl_with_attribute_on_method() { + let src = " + impl Foo { + #[something] + fn foo(self) {} + } + "; + let type_impl = parse_type_impl_no_errors(src); + let attributes = type_impl.methods[0].0.item.attributes(); + assert_eq!(attributes.secondary.len(), 1); + } + + #[test] + fn parse_impl_with_self_argument() { + let src = "impl Foo { fn foo(self) {} }"; + let mut type_impl = parse_type_impl_no_errors(src); + assert_eq!(type_impl.methods.len(), 1); + + let (method, _) = type_impl.methods.remove(0); + let mut method = method.item; + assert_eq!(method.def.name.to_string(), "foo"); + assert_eq!(method.def.parameters.len(), 1); + + let param = method.def.parameters.remove(0); + let Pattern::Identifier(name) = param.pattern else { + panic!("Expected identifier pattern"); + }; + assert_eq!(name.to_string(), "self"); + assert_eq!(param.typ.to_string(), "Self"); + } + + #[test] + fn parse_impl_with_mut_self_argument() { + let src = "impl Foo { fn foo(mut self) {} }"; + let mut type_impl = parse_type_impl_no_errors(src); + assert_eq!(type_impl.methods.len(), 1); + + let (method, _) = type_impl.methods.remove(0); + let mut method = method.item; + assert_eq!(method.def.name.to_string(), "foo"); + assert_eq!(method.def.parameters.len(), 1); + + let param = method.def.parameters.remove(0); + let Pattern::Mutable(pattern, _, true) = param.pattern else { + panic!("Expected mutable pattern"); + }; + let pattern: &Pattern = &pattern; + let Pattern::Identifier(name) = pattern else { + panic!("Expected identifier pattern"); + }; + assert_eq!(name.to_string(), "self"); + assert_eq!(param.typ.to_string(), "Self"); + } + + #[test] + fn parse_impl_with_reference_mut_self_argument() { + let src = "impl Foo { fn foo(&mut self) {} }"; + let mut type_impl = parse_type_impl_no_errors(src); + assert_eq!(type_impl.methods.len(), 1); + + let (method, _) = type_impl.methods.remove(0); + let mut method = method.item; + assert_eq!(method.def.name.to_string(), "foo"); + assert_eq!(method.def.parameters.len(), 1); + + let param = method.def.parameters.remove(0); + let Pattern::Identifier(name) = param.pattern else { + panic!("Expected identifier pattern"); + }; + assert_eq!(name.to_string(), "self"); + assert_eq!(param.typ.to_string(), "&mut Self"); + } + + #[test] + fn parse_impl_with_self_argument_followed_by_type() { + let src = "impl Foo { fn foo(self: Foo) {} }"; + let mut type_impl = parse_type_impl_no_errors(src); + assert_eq!(type_impl.methods.len(), 1); + + let (method, _) = type_impl.methods.remove(0); + let mut method = method.item; + assert_eq!(method.def.name.to_string(), "foo"); + assert_eq!(method.def.parameters.len(), 1); + + let param = method.def.parameters.remove(0); + let Pattern::Identifier(name) = param.pattern else { + panic!("Expected identifier pattern"); + }; + assert_eq!(name.to_string(), "self"); + assert_eq!(param.typ.to_string(), "Foo"); + } + + #[test] + fn parse_empty_impl_missing_right_brace() { + let src = "impl Foo {"; + let (module, errors) = parse_program(src); + assert_eq!(errors.len(), 1); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Impl(type_impl) = &item.kind else { + panic!("Expected type impl"); + }; + assert_eq!(type_impl.object_type.to_string(), "Foo"); + } + + #[test] + fn parse_empty_impl_incorrect_body() { + let src = "impl Foo { hello fn foo() {} }"; + let (module, errors) = parse_program(src); + assert_eq!(errors.len(), 1); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Impl(type_impl) = &item.kind else { + panic!("Expected type impl"); + }; + assert_eq!(type_impl.object_type.to_string(), "Foo"); + assert_eq!(type_impl.methods.len(), 1); + } + + #[test] + fn parse_empty_trait_impl() { + let src = "impl Foo for Field {}"; + let trait_impl = parse_trait_impl_no_errors(src); + assert_eq!(trait_impl.trait_name.to_string(), "Foo"); + assert!(matches!(trait_impl.object_type.typ, UnresolvedTypeData::FieldElement)); + assert!(trait_impl.items.is_empty()); + assert!(trait_impl.impl_generics.is_empty()); + } + + #[test] + fn parse_empty_trait_impl_with_generics() { + let src = "impl Foo for Field {}"; + let trait_impl = parse_trait_impl_no_errors(src); + assert_eq!(trait_impl.trait_name.to_string(), "Foo"); + assert!(matches!(trait_impl.object_type.typ, UnresolvedTypeData::FieldElement)); + assert!(trait_impl.items.is_empty()); + assert_eq!(trait_impl.impl_generics.len(), 1); + } + + #[test] + fn parse_trait_impl_with_function() { + let src = "impl Foo for Field { fn foo() {} }"; + let mut trait_impl = parse_trait_impl_no_errors(src); + assert_eq!(trait_impl.trait_name.to_string(), "Foo"); + assert_eq!(trait_impl.items.len(), 1); + + let item = trait_impl.items.remove(0).item; + let TraitImplItemKind::Function(function) = item.kind else { + panic!("Expected function"); + }; + assert_eq!(function.def.name.to_string(), "foo"); + assert_eq!(function.def.visibility, ItemVisibility::Public); + } + + #[test] + fn parse_trait_impl_with_generic_type_args() { + let src = "impl Foo for Field { }"; + let trait_impl = parse_trait_impl_no_errors(src); + assert_eq!(trait_impl.trait_name.to_string(), "Foo"); + assert!(!trait_impl.trait_generics.is_empty()); + } + + #[test] + fn parse_trait_impl_with_type() { + let src = "impl Foo for Field { type Foo = i32; }"; + let mut trait_impl = parse_trait_impl_no_errors(src); + assert_eq!(trait_impl.trait_name.to_string(), "Foo"); + assert_eq!(trait_impl.items.len(), 1); + + let item = trait_impl.items.remove(0).item; + let TraitImplItemKind::Type { name, alias } = item.kind else { + panic!("Expected type"); + }; + assert_eq!(name.to_string(), "Foo"); + assert_eq!(alias.to_string(), "i32"); + } + + #[test] + fn parse_trait_impl_with_let() { + let src = "impl Foo for Field { let x: Field = 1; }"; + let mut trait_impl = parse_trait_impl_no_errors(src); + assert_eq!(trait_impl.trait_name.to_string(), "Foo"); + assert_eq!(trait_impl.items.len(), 1); + + let item = trait_impl.items.remove(0).item; + let TraitImplItemKind::Constant(name, typ, expr) = item.kind else { + panic!("Expected constant"); + }; + assert_eq!(name.to_string(), "x"); + assert_eq!(typ.to_string(), "Field"); + assert_eq!(expr.to_string(), "1"); + } + + #[test] + fn recovers_on_unknown_impl_item() { + let src = " + impl Foo { hello fn foo() {} } + ^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Impl(type_impl) = &item.kind else { + panic!("Expected impl"); + }; + assert_eq!(type_impl.methods.len(), 1); + + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a fn but found hello"); + } + + #[test] + fn recovers_on_unknown_trait_impl_item() { + let src = " + impl Foo for i32 { hello fn foo() {} } + ^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::TraitImpl(trait_imp) = &item.kind else { + panic!("Expected trait impl"); + }; + assert_eq!(trait_imp.items.len(), 1); + + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a trait impl item but found hello"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs new file mode 100644 index 00000000000..f006923b8a2 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -0,0 +1,193 @@ +use noirc_errors::{Span, Spanned}; + +use crate::{ + ast::{BinaryOpKind, Expression, ExpressionKind, InfixExpression}, + token::Token, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + /// EqualOrNotEqualExpression + /// = OrExpression ( ( '==' | '!=' ) OrExpression )* + pub(super) fn parse_equal_or_not_equal( + &mut self, + allow_constructors: bool, + ) -> Option { + self.parse_infix(allow_constructors, Parser::parse_or, |parser| { + if parser.eat(Token::Equal) { + Some(BinaryOpKind::Equal) + } else if parser.eat(Token::NotEqual) { + Some(BinaryOpKind::NotEqual) + } else { + None + } + }) + } + + /// OrExpression + /// = AndExpression ( '|' AndExpression )* + pub(super) fn parse_or(&mut self, allow_constructors: bool) -> Option { + self.parse_infix(allow_constructors, Parser::parse_and, |parser| { + // Don't parse `x |= ...`, etc. + if parser.next_is(Token::Assign) { + None + } else if parser.eat(Token::Pipe) { + Some(BinaryOpKind::Or) + } else { + None + } + }) + } + + /// AndExpression + /// = XorExpression ( '&' XorExpression )* + pub(super) fn parse_and(&mut self, allow_constructors: bool) -> Option { + self.parse_infix(allow_constructors, Parser::parse_xor, |parser| { + // Don't parse `x |= ...`, etc. + if parser.next_is(Token::Assign) { + None + } else if parser.eat(Token::Ampersand) { + Some(BinaryOpKind::And) + } else { + None + } + }) + } + + /// XorExpression + /// = LessOrGreaterExpression ( '^' LessOrGreaterExpression )* + pub(super) fn parse_xor(&mut self, allow_constructors: bool) -> Option { + self.parse_infix(allow_constructors, Parser::parse_less_or_greater, |parser| { + // Don't parse `x |= ...`, etc. + if parser.next_is(Token::Assign) { + None + } else if parser.eat(Token::Caret) { + Some(BinaryOpKind::Xor) + } else { + None + } + }) + } + + /// LessOrGreaterExpression + /// = ShiftExpression ( ( '<' | '<=' | '>' | '>=' ) ShiftExpression )* + pub(super) fn parse_less_or_greater(&mut self, allow_constructors: bool) -> Option { + self.parse_infix(allow_constructors, Parser::parse_shift, |parser| { + if parser.eat(Token::Less) { + Some(BinaryOpKind::Less) + } else if parser.eat(Token::LessEqual) { + Some(BinaryOpKind::LessEqual) + } else if parser.next_token.token() != &Token::GreaterEqual + && parser.eat(Token::Greater) + { + // Make sure to skip the `>>=` case, as `>>=` is lexed as `> >=`. + Some(BinaryOpKind::Greater) + } else if parser.eat(Token::GreaterEqual) { + Some(BinaryOpKind::GreaterEqual) + } else { + None + } + }) + } + + /// ShiftExpression + /// = AddOrSubtractExpression ( ( '<<' | '>' '>' ) AddOrSubtractExpression )* + pub(super) fn parse_shift(&mut self, allow_constructors: bool) -> Option { + self.parse_infix(allow_constructors, Parser::parse_add_or_subtract, |parser| { + if !parser.next_is(Token::Assign) && parser.eat(Token::ShiftLeft) { + Some(BinaryOpKind::ShiftLeft) + } else if parser.at(Token::Greater) && parser.next_is(Token::Greater) { + // Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier + // to parse nested generic types. For normal expressions however, it means we have to manually + // parse two greater-than tokens as a single right-shift here. + parser.bump(); + parser.bump(); + Some(BinaryOpKind::ShiftRight) + } else { + None + } + }) + } + + /// AddOrSubtractExpression + /// = MultiplyOrDivideOrModuloExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloExpression )* + pub(super) fn parse_add_or_subtract(&mut self, allow_constructors: bool) -> Option { + self.parse_infix(allow_constructors, Parser::parse_multiply_or_divide_or_modulo, |parser| { + if parser.next_is(Token::Assign) { + None + } else if parser.eat(Token::Plus) { + Some(BinaryOpKind::Add) + } else if parser.eat(Token::Minus) { + Some(BinaryOpKind::Subtract) + } else { + None + } + }) + } + + /// MultiplyOrDivideOrModuloExpression + /// = Term ( ( '*' | '/' | '%' ) Term )* + pub(super) fn parse_multiply_or_divide_or_modulo( + &mut self, + allow_constructors: bool, + ) -> Option { + self.parse_infix(allow_constructors, Parser::parse_term, |parser| { + if parser.next_is(Token::Assign) { + None + } else if parser.eat(Token::Star) { + Some(BinaryOpKind::Multiply) + } else if parser.eat(Token::Slash) { + Some(BinaryOpKind::Divide) + } else if parser.eat(Token::Percent) { + Some(BinaryOpKind::Modulo) + } else { + None + } + }) + } + + fn parse_infix( + &mut self, + allow_constructors: bool, + mut next: Next, + mut op: Op, + ) -> Option + where + Next: FnMut(&mut Parser<'a>, bool) -> Option, + Op: FnMut(&mut Parser<'a>) -> Option, + { + let start_span = self.current_token_span; + let mut lhs = next(self, allow_constructors)?; + + loop { + let operator_start_span = self.current_token_span; + let Some(operator) = op(self) else { + break; + }; + let operator = Spanned::from(operator_start_span, operator); + + let Some(rhs) = next(self, allow_constructors) else { + self.push_expected_expression(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + fn new_infix_expression( + &self, + lhs: Expression, + operator: Spanned, + rhs: Expression, + start_span: Span, + ) -> Expression { + let infix_expr = InfixExpression { lhs, operator, rhs }; + let kind = ExpressionKind::Infix(Box::new(infix_expr)); + let span = self.span_since(start_span); + Expression { kind, span } + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs new file mode 100644 index 00000000000..0faa2ba80ee --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -0,0 +1,242 @@ +use crate::{ + parser::{labels::ParsingRuleLabel, Item, ItemKind}, + token::{Keyword, Token}, +}; + +use super::{impls::Impl, parse_many::without_separator, Parser}; + +impl<'a> Parser<'a> { + pub(crate) fn parse_top_level_items(&mut self) -> Vec { + self.parse_module_items( + false, // nested + ) + } + + pub(crate) fn parse_module_items(&mut self, nested: bool) -> Vec { + self.parse_many("items", without_separator(), |parser| { + parser.parse_module_item_in_list(nested) + }) + } + + fn parse_module_item_in_list(&mut self, nested: bool) -> Option { + loop { + // We only break out of the loop on `}` if we are inside a `mod { ..` + if nested && self.at(Token::RightBrace) { + return None; + } + + // We always break on EOF (we don't error because if we are inside `mod { ..` + // the outer parsing logic will error instead) + if self.at_eof() { + return None; + } + + let Some(item) = self.parse_item() else { + // If we couldn't parse an item we check which token we got + match self.token.token() { + Token::RightBrace if nested => { + return None; + } + Token::EOF => return None, + _ => (), + } + + self.expected_label(ParsingRuleLabel::Item); + // We'll try parsing an item starting on the next token + self.bump(); + continue; + }; + + return Some(item); + } + } + + /// Parses an item inside an impl or trait, with good recovery: + /// - If we run into EOF, we error that we expect a '}' + /// - If we can't parse an item and we don't end up in '}', error but try with the next token + pub(super) fn parse_item_in_list( + &mut self, + label: ParsingRuleLabel, + mut f: F, + ) -> Option + where + F: FnMut(&mut Parser<'a>) -> Option, + { + loop { + if self.at_eof() { + self.expected_token(Token::RightBrace); + return None; + } + + let Some(item) = f(self) else { + if !self.at(Token::RightBrace) { + self.expected_label(label.clone()); + + // Try with the next token + self.bump(); + continue; + } + + return None; + }; + + return Some(item); + } + } + + /// Item = OuterDocComments ItemKind + fn parse_item(&mut self) -> Option { + let start_span = self.current_token_span; + let doc_comments = self.parse_outer_doc_comments(); + let kind = self.parse_item_kind()?; + let span = self.span_since(start_span); + + Some(Item { kind, span, doc_comments }) + } + + /// ItemKind + /// = InnerAttribute + /// | Attributes Modifiers + /// ( Use + /// | ModOrContract + /// | Struct + /// | Impl + /// | Trait + /// | Global + /// | TypeAlias + /// | Function + /// ) + fn parse_item_kind(&mut self) -> Option { + if let Some(kind) = self.parse_inner_attribute() { + return Some(ItemKind::InnerAttribute(kind)); + } + + let start_span = self.current_token_span; + let attributes = self.parse_attributes(); + + let modifiers = self.parse_modifiers( + true, // allow mut + ); + + if self.eat_keyword(Keyword::Use) { + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + + let use_tree = self.parse_use_tree(); + return Some(ItemKind::Import(use_tree, modifiers.visibility)); + } + + if let Some(is_contract) = self.eat_mod_or_contract() { + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + + return Some(self.parse_mod_or_contract(attributes, is_contract, modifiers.visibility)); + } + + if self.eat_keyword(Keyword::Struct) { + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + + return Some(ItemKind::Struct(self.parse_struct( + attributes, + modifiers.visibility, + start_span, + ))); + } + + if self.eat_keyword(Keyword::Impl) { + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + + return Some(match self.parse_impl() { + Impl::Impl(type_impl) => ItemKind::Impl(type_impl), + Impl::TraitImpl(noir_trait_impl) => ItemKind::TraitImpl(noir_trait_impl), + }); + } + + if self.eat_keyword(Keyword::Trait) { + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + + return Some(ItemKind::Trait(self.parse_trait( + attributes, + modifiers.visibility, + start_span, + ))); + } + + if self.eat_keyword(Keyword::Global) { + self.unconstrained_not_applicable(modifiers); + + return Some(ItemKind::Global( + self.parse_global( + attributes, + modifiers.comptime.is_some(), + modifiers.mutable.is_some(), + ), + modifiers.visibility, + )); + } + + if self.eat_keyword(Keyword::Type) { + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + + return Some(ItemKind::TypeAlias( + self.parse_type_alias(modifiers.visibility, start_span), + )); + } + + if self.eat_keyword(Keyword::Fn) { + self.mutable_not_applicable(modifiers); + + return Some(ItemKind::Function(self.parse_function( + attributes, + modifiers.visibility, + modifiers.comptime.is_some(), + modifiers.unconstrained.is_some(), + false, // allow_self + ))); + } + + None + } + + fn eat_mod_or_contract(&mut self) -> Option { + if self.eat_keyword(Keyword::Mod) { + Some(false) + } else if self.eat_keyword(Keyword::Contract) { + Some(true) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + parse_program, + parser::parser::tests::{get_single_error, get_source_with_error_span}, + }; + + #[test] + fn recovers_on_unknown_item() { + let src = " + fn foo() {} hello fn bar() {} + ^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + assert_eq!(module.items.len(), 2); + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected an item but found hello"); + } + + #[test] + fn errors_on_eof_in_nested_mod() { + let src = " + mod foo { fn foo() {} + ^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + assert_eq!(module.items.len(), 1); + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a } but found end of input"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs new file mode 100644 index 00000000000..1731284e354 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -0,0 +1,114 @@ +use crate::{ + ast::ItemVisibility, + token::{Keyword, Token}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + /// ItemVisibility + /// = 'pub' // ItemVisibility::Public + /// | 'pub' '(' 'crate' ')' // ItemVisibility::PublicCrate + /// | nothing // ItemVisibility::Private + pub(super) fn parse_item_visibility(&mut self) -> ItemVisibility { + if !self.eat_keyword(Keyword::Pub) { + return ItemVisibility::Private; + } + + if !self.eat_left_paren() { + // `pub` + return ItemVisibility::Public; + } + + if !self.eat_keyword(Keyword::Crate) { + // `pub(` or `pub()` + self.expected_token(Token::Keyword(Keyword::Crate)); + self.eat_right_paren(); + return ItemVisibility::Public; + } + + self.eat_or_error(Token::RightParen); + + // `pub(crate)`` + ItemVisibility::PublicCrate + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::ItemVisibility, + parser::{ + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + Parser, + }, + }; + + #[test] + fn parses_private_visibility() { + let src = "("; + let mut parser = Parser::for_str(src); + let visibility = parser.parse_item_visibility(); + expect_no_errors(&parser.errors); + assert_eq!(visibility, ItemVisibility::Private); + } + + #[test] + fn parses_public_visibility() { + let src = "pub"; + let mut parser = Parser::for_str(src); + let visibility = parser.parse_item_visibility(); + expect_no_errors(&parser.errors); + assert_eq!(visibility, ItemVisibility::Public); + } + + #[test] + fn parses_public_visibility_unclosed_parentheses() { + let src = " + pub( + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let visibility = parser.parse_item_visibility(); + assert_eq!(visibility, ItemVisibility::Public); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a crate but found end of input"); + } + + #[test] + fn parses_public_visibility_no_crate_after_pub() { + let src = " + pub(hello + ^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let visibility = parser.parse_item_visibility(); + assert_eq!(visibility, ItemVisibility::Public); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a crate but found hello"); + } + #[test] + fn parses_public_visibility_missing_paren_after_pub_crate() { + let src = " + pub(crate + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let visibility = parser.parse_item_visibility(); + assert_eq!(visibility, ItemVisibility::PublicCrate); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a ) but found end of input"); + } + + #[test] + fn parses_public_crate_visibility() { + let src = "pub(crate)"; + let mut parser = Parser::for_str(src); + let visibility = parser.parse_item_visibility(); + expect_no_errors(&parser.errors); + assert_eq!(visibility, ItemVisibility::PublicCrate); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs new file mode 100644 index 00000000000..a6eeb428621 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -0,0 +1,58 @@ +use crate::{ + ast::{ExpressionKind, Lambda, Pattern, UnresolvedType}, + parser::labels::ParsingRuleLabel, + token::Token, +}; + +use super::{parse_many::separated_by_comma, Parser}; + +impl<'a> Parser<'a> { + /// Lambda = '|' LambdaParameters? '|' ( '->' Type )? Expression + /// + /// LambdaParameters = LambdaParameter ( ',' LambdaParameter )? ','? + /// + /// LambdaParameter + /// = Pattern OptionalTypeAnnotation + pub(super) fn parse_lambda(&mut self) -> Option { + if !self.eat_pipe() { + return None; + } + + let parameters = self.parse_lambda_parameters(); + let return_type = if self.eat(Token::Arrow) { + self.parse_type_or_error() + } else { + self.unspecified_type_at_previous_token_end() + }; + let body = self.parse_expression_or_error(); + + Some(ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body }))) + } + + fn parse_lambda_parameters(&mut self) -> Vec<(Pattern, UnresolvedType)> { + self.parse_many( + "parameters", + separated_by_comma().until(Token::Pipe), + Self::parse_lambda_parameter, + ) + } + + fn parse_lambda_parameter(&mut self) -> Option<(Pattern, UnresolvedType)> { + loop { + let Some(pattern) = self.parse_pattern() else { + self.expected_label(ParsingRuleLabel::Pattern); + + // Let's try with the next token. + self.bump(); + if self.at_eof() { + return None; + } else { + continue; + } + }; + + let typ = self.parse_optional_type_annotation(); + return Some((pattern, typ)); + } + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/lambdas.rs b/compiler/noirc_frontend/src/parser/parser/lambdas.rs deleted file mode 100644 index 68b5724edc6..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/lambdas.rs +++ /dev/null @@ -1,42 +0,0 @@ -use chumsky::{primitive::just, Parser}; - -use super::{parse_type, pattern}; -use crate::ast::{Expression, ExpressionKind, Lambda, Pattern, UnresolvedType, UnresolvedTypeData}; -use crate::{ - parser::{labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, NoirParser}, - token::Token, -}; - -pub(super) fn lambda<'a>( - expr_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - lambda_parameters() - .delimited_by(just(Token::Pipe), just(Token::Pipe)) - .then(lambda_return_type()) - .then(expr_parser) - .map(|((parameters, return_type), body)| { - ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) - }) -} - -fn lambda_parameters() -> impl NoirParser> { - let typ = parse_type().recover_via(parameter_recovery()); - let typ = just(Token::Colon).ignore_then(typ); - - let parameter = - pattern().recover_via(parameter_name_recovery()).then(typ.or_not().map_with_span( - |typ, span| typ.unwrap_or(UnresolvedTypeData::Unspecified.with_span(span)), - )); - - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} - -fn lambda_return_type() -> impl NoirParser { - just(Token::Arrow) - .ignore_then(parse_type()) - .or_not() - .map_with_span(|ret, span| ret.unwrap_or(UnresolvedTypeData::Unspecified.with_span(span))) -} diff --git a/compiler/noirc_frontend/src/parser/parser/literals.rs b/compiler/noirc_frontend/src/parser/parser/literals.rs deleted file mode 100644 index b25b6acc9e2..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/literals.rs +++ /dev/null @@ -1,158 +0,0 @@ -use chumsky::Parser; - -use crate::{ - ast::ExpressionKind, - parser::NoirParser, - token::{Token, TokenKind}, -}; - -use super::primitives::token_kind; - -pub(super) fn literal() -> impl NoirParser { - token_kind(TokenKind::Literal).map(|token| match token { - Token::Int(x) => ExpressionKind::integer(x), - Token::Bool(b) => ExpressionKind::boolean(b), - Token::Str(s) => ExpressionKind::string(s), - Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes), - Token::FmtStr(s) => ExpressionKind::format_string(s), - unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected), - }) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::ast::Literal; - use crate::parser::parser::{ - expression, expression_no_constructors, fresh_statement, term, test_helpers::*, - }; - - fn expr_to_lit(expr: ExpressionKind) -> Literal { - match expr { - ExpressionKind::Literal(literal) => literal, - _ => unreachable!("expected a literal"), - } - } - - #[test] - fn parse_int() { - let int = parse_with(literal(), "5").unwrap(); - let hex = parse_with(literal(), "0x05").unwrap(); - - match (expr_to_lit(int), expr_to_lit(hex)) { - (Literal::Integer(int, false), Literal::Integer(hex, false)) => assert_eq!(int, hex), - _ => unreachable!(), - } - } - - #[test] - fn parse_string() { - let expr = parse_with(literal(), r#""hello""#).unwrap(); - match expr_to_lit(expr) { - Literal::Str(s) => assert_eq!(s, "hello"), - _ => unreachable!(), - }; - } - - #[test] - fn parse_bool() { - let expr_true = parse_with(literal(), "true").unwrap(); - let expr_false = parse_with(literal(), "false").unwrap(); - - match (expr_to_lit(expr_true), expr_to_lit(expr_false)) { - (Literal::Bool(t), Literal::Bool(f)) => { - assert!(t); - assert!(!f); - } - _ => unreachable!(), - }; - } - - #[test] - fn parse_unary() { - parse_all( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], - ); - parse_all_failing( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["+hello", "/hello"], - ); - } - - #[test] - fn parse_raw_string_expr() { - let cases = vec![ - Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, - Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, - // backslash - Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, - Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, - Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, - Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, - // escape sequence - Case { - source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, - expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, - errors: 0, - }, - Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, - // mismatch - errors: - Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - // mismatch: short: - Case { source: r##" r"foo"# "##, expect: r#"r"foo""#, errors: 1 }, - Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 }, - // empty string - Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 }, - #[allow(clippy::needless_raw_string_hashes)] - Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, - // miscellaneous - Case { source: r##" r#\"foo\"# "##, expect: "r", errors: 2 }, - Case { source: r#" r\"foo\" "#, expect: "r", errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - // missing 'r' letter - Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 }, - Case { source: r#" #"foo" "#, expect: "foo", errors: 2 }, - // whitespace - Case { source: r##" r #"foo"# "##, expect: "r", errors: 2 }, - Case { source: r##" r# "foo"# "##, expect: "r", errors: 3 }, - Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 }, - // after identifier - Case { source: r##" bar#"foo"# "##, expect: "bar", errors: 2 }, - // nested - Case { - source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, - expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, - errors: 0, - }, - ]; - - check_cases_with_errors(&cases[..], expression()); - } - - #[test] - fn parse_raw_string_lit() { - let lit_cases = vec![ - Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, - Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, - // backslash - Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, - Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, - Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, - Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, - // escape sequence - Case { - source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, - expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, - errors: 0, - }, - Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, - // mismatch - errors: - Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - ]; - - check_cases_with_errors(&lit_cases[..], literal()); - } -} diff --git a/compiler/noirc_frontend/src/parser/parser/modifiers.rs b/compiler/noirc_frontend/src/parser/parser/modifiers.rs new file mode 100644 index 00000000000..d3bf692ee53 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -0,0 +1,39 @@ +use noirc_errors::Span; + +use crate::{ast::ItemVisibility, token::Keyword}; + +use super::Parser; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) struct Modifiers { + pub(crate) visibility: ItemVisibility, + pub(crate) visibility_span: Span, + pub(crate) unconstrained: Option, + pub(crate) comptime: Option, + pub(crate) mutable: Option, +} + +impl<'a> Parser<'a> { + /// Modifiers = 'unconstrained'? ItemVisibility 'comptime'? 'mut'? + pub(crate) fn parse_modifiers(&mut self, allow_mutable: bool) -> Modifiers { + let unconstrained = if self.eat_keyword(Keyword::Unconstrained) { + Some(self.previous_token_span) + } else { + None + }; + + let start_span = self.current_token_span; + let visibility = self.parse_item_visibility(); + let visibility_span = self.span_since(start_span); + + let comptime = + if self.eat_keyword(Keyword::Comptime) { Some(self.previous_token_span) } else { None }; + let mutable = if allow_mutable && self.eat_keyword(Keyword::Mut) { + Some(self.previous_token_span) + } else { + None + }; + + Modifiers { visibility, visibility_span, unconstrained, comptime, mutable } + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/module.rs b/compiler/noirc_frontend/src/parser/parser/module.rs new file mode 100644 index 00000000000..263338863c0 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -0,0 +1,104 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Ident, ItemVisibility, ModuleDeclaration}, + parser::{ItemKind, ParsedSubModule}, + token::{Attribute, Token}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + /// ModOrContract + /// = ( 'mod' | 'contract' ) identifier ( '{' Module '}' | ';' ) + pub(super) fn parse_mod_or_contract( + &mut self, + attributes: Vec<(Attribute, Span)>, + is_contract: bool, + visibility: ItemVisibility, + ) -> ItemKind { + let outer_attributes = self.validate_secondary_attributes(attributes); + + let Some(ident) = self.eat_ident() else { + self.expected_identifier(); + return ItemKind::ModuleDecl(ModuleDeclaration { + visibility, + ident: Ident::default(), + outer_attributes, + }); + }; + + if self.eat_left_brace() { + let contents = self.parse_module( + true, // nested + ); + self.eat_or_error(Token::RightBrace); + ItemKind::Submodules(ParsedSubModule { + visibility, + name: ident, + contents, + outer_attributes, + is_contract, + }) + } else { + if !self.eat_semicolons() { + self.expected_token(Token::Semicolon); + } + ItemKind::ModuleDecl(ModuleDeclaration { visibility, ident, outer_attributes }) + } + } +} + +#[cfg(test)] +mod tests { + use crate::parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }; + + #[test] + fn parse_module_declaration() { + // TODO: `contract foo;` is parsed correctly but we don't it's considered a module + let src = "mod foo;"; + let (module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::ModuleDecl(module) = &item.kind else { + panic!("Expected module declaration"); + }; + assert_eq!("foo", module.ident.to_string()); + } + + #[test] + fn parse_submodule() { + let src = "mod foo { mod bar; }"; + let (module, errors) = parse_program(src); + dbg!(&errors); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Submodules(parsed_submodule) = &item.kind else { + panic!("Expected submodules declaration"); + }; + assert!(!parsed_submodule.is_contract); + assert_eq!("foo", parsed_submodule.name.to_string()); + assert_eq!(parsed_submodule.contents.items.len(), 1); + } + + #[test] + fn parse_contract() { + let src = "contract foo {}"; + let (module, errors) = parse_program(src); + dbg!(&errors); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Submodules(parsed_submodule) = &item.kind else { + panic!("Expected submodules declaration"); + }; + assert!(parsed_submodule.is_contract); + assert_eq!("foo", parsed_submodule.name.to_string()); + assert_eq!(parsed_submodule.contents.items.len(), 0); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/parse_many.rs b/compiler/noirc_frontend/src/parser/parser/parse_many.rs new file mode 100644 index 00000000000..ea4dfe97122 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -0,0 +1,108 @@ +use crate::token::Token; + +use super::Parser; + +impl<'a> Parser<'a> { + /// Parses a list of items separated by a token, optionally ending when another token is found. + /// The given function `f` must parse one item (eventually parsing many, if separators are found). + /// If no item is parsed, `f` must report an error and return `None`. + pub(super) fn parse_many( + &mut self, + items: &'static str, + separated_by: SeparatedBy, + f: F, + ) -> Vec + where + F: FnMut(&mut Parser<'a>) -> Option, + { + self.parse_many_return_trailing_separator_if_any(items, separated_by, f).0 + } + + /// Same as parse_many, but returns a bool indicating whether a trailing separator was found. + pub(super) fn parse_many_return_trailing_separator_if_any( + &mut self, + items: &'static str, + separated_by: SeparatedBy, + mut f: F, + ) -> (Vec, bool) + where + F: FnMut(&mut Parser<'a>) -> Option, + { + let mut elements: Vec = Vec::new(); + let mut trailing_separator = false; + loop { + if let Some(end) = &separated_by.until { + if self.eat(end.clone()) { + break; + } + } + + let start_span = self.current_token_span; + let Some(element) = f(self) else { + if let Some(end) = &separated_by.until { + self.eat(end.clone()); + } + break; + }; + + if let Some(separator) = &separated_by.token { + if !trailing_separator && !elements.is_empty() { + self.expected_token_separating_items(separator.clone(), items, start_span); + } + } + + elements.push(element); + + trailing_separator = if let Some(separator) = &separated_by.token { + self.eat(separator.clone()) + } else { + true + }; + + if !trailing_separator && !separated_by.continue_if_separator_is_missing { + if let Some(end) = &separated_by.until { + self.eat(end.clone()); + } + break; + } + } + + (elements, trailing_separator) + } +} + +pub(super) struct SeparatedBy { + pub(super) token: Option, + pub(super) until: Option, + pub(super) continue_if_separator_is_missing: bool, +} + +impl SeparatedBy { + pub(super) fn until(self, token: Token) -> SeparatedBy { + SeparatedBy { until: Some(token), ..self } + } + + pub(super) fn stop_if_separator_is_missing(self) -> SeparatedBy { + SeparatedBy { continue_if_separator_is_missing: false, ..self } + } +} + +pub(super) fn separated_by(token: Token) -> SeparatedBy { + SeparatedBy { token: Some(token), until: None, continue_if_separator_is_missing: true } +} + +pub(super) fn separated_by_comma() -> SeparatedBy { + separated_by(Token::Comma) +} + +pub(super) fn separated_by_comma_until_right_paren() -> SeparatedBy { + separated_by_comma().until(Token::RightParen) +} + +pub(super) fn separated_by_comma_until_right_brace() -> SeparatedBy { + separated_by_comma().until(Token::RightBrace) +} + +pub(super) fn without_separator() -> SeparatedBy { + SeparatedBy { token: None, until: None, continue_if_separator_is_missing: true } +} diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 4babd0f6730..99aedc6df89 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,172 +1,356 @@ -use crate::ast::{ - AsTraitPath, ExpressionKind, Ident, Path, PathKind, PathSegment, TypePath, UnresolvedType, -}; -use crate::parser::{NoirParser, ParserError, ParserErrorReason}; +use crate::ast::{AsTraitPath, Ident, Path, PathKind, PathSegment, UnresolvedType}; +use crate::parser::ParserErrorReason; use crate::token::{Keyword, Token}; -use chumsky::prelude::*; use noirc_errors::Span; -use super::keyword; -use super::primitives::{ident, path_segment, path_segment_no_turbofish, turbofish}; -use super::types::{generic_type_args, primitive_type}; +use crate::{parser::labels::ParsingRuleLabel, token::TokenKind}; -pub(super) fn path<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - path_inner(path_segment(type_parser)) -} +use super::Parser; -pub fn path_no_turbofish() -> impl NoirParser { - path_inner(path_segment_no_turbofish()) -} +impl<'a> Parser<'a> { + #[cfg(test)] + pub(crate) fn parse_path_or_error(&mut self) -> Path { + if let Some(path) = self.parse_path() { + path + } else { + self.expected_label(ParsingRuleLabel::Path); -fn path_inner<'a>(segment: impl NoirParser + 'a) -> impl NoirParser + 'a { - let segments = segment - .separated_by(just(Token::DoubleColon)) - .at_least(1) - .then(just(Token::DoubleColon).then_ignore(none_of(Token::LeftBrace).rewind()).or_not()) - .validate(|(path_segments, trailing_colons), span, emit_error| { - if trailing_colons.is_some() { - emit_error(ParserError::with_reason( - ParserErrorReason::ExpectedIdentifierAfterColons, - span, - )); + Path { + segments: Vec::new(), + kind: PathKind::Plain, + span: self.span_at_previous_token_end(), } - path_segments - }); - let make_path = |kind| move |segments, span| Path { segments, kind, span }; - - let prefix = |key| keyword(key).ignore_then(just(Token::DoubleColon)); - let path_kind = - |key, kind| prefix(key).ignore_then(segments.clone()).map_with_span(make_path(kind)); - - choice(( - path_kind(Keyword::Crate, PathKind::Crate), - path_kind(Keyword::Dep, PathKind::Dep), - path_kind(Keyword::Super, PathKind::Super), - segments.map_with_span(make_path(PathKind::Plain)), - )) -} + } + } -/// Parses `::path_segment` -/// These paths only support exactly two segments. -pub(super) fn as_trait_path<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - just(Token::Less) - .ignore_then(type_parser.clone()) - .then_ignore(keyword(Keyword::As)) - .then(path(type_parser.clone())) - .then(generic_type_args(type_parser)) - .then_ignore(just(Token::Greater)) - .then_ignore(just(Token::DoubleColon)) - .then(ident()) - .map(|(((typ, trait_path), trait_generics), impl_item)| AsTraitPath { - typ, - trait_path, - trait_generics, - impl_item, - }) -} + /// Tries to parse a Path. + /// Note that `crate::`, `super::`, etc., are not valid paths on their own. + /// + /// Path = PathKind identifier Turbofish? ( '::' identifier Turbofish? )* + /// + /// Turbofish = '::' PathGenerics + pub(crate) fn parse_path(&mut self) -> Option { + self.parse_path_impl( + true, // allow turbofish + true, // allow trailing double colon + ) + } -/// Parses `MyType::path_segment` -/// These paths only support exactly two segments. -/// Unlike normal paths `MyType` here can also be a primitive type or interned type -/// in addition to a named type. -pub(super) fn type_path<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - primitive_type() - .then_ignore(just(Token::DoubleColon)) - .then(ident().or_not()) - .then(turbofish(type_parser)) - .validate(|((typ, item), turbofish), span, emit| { - let turbofish = turbofish.unwrap_or_default(); - let item = if let Some(item) = item { - item - } else { - emit(ParserError::with_reason( - ParserErrorReason::ExpectedIdentifierAfterColons, - span, - )); - Ident::new(String::new(), Span::from(span.end()..span.end())) - }; - ExpressionKind::TypePath(TypePath { typ, item, turbofish }) - }) -} + pub(crate) fn parse_path_no_turbofish_or_error(&mut self) -> Path { + if let Some(path) = self.parse_path_no_turbofish() { + path + } else { + self.expected_label(ParsingRuleLabel::Path); + + Path { + segments: Vec::new(), + kind: PathKind::Plain, + span: self.span_at_previous_token_end(), + } + } + } -fn empty_path() -> impl NoirParser { - let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; - let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); + /// PathNoTurbofish = PathKind identifier ( '::' identifier )* + pub fn parse_path_no_turbofish(&mut self) -> Option { + self.parse_path_impl( + false, // allow turbofish + true, // allow trailing double colon + ) + } - choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Plain))) -} + pub(super) fn parse_path_impl( + &mut self, + allow_turbofish: bool, + allow_trailing_double_colon: bool, + ) -> Option { + let start_span = self.current_token_span; + + let kind = self.parse_path_kind(); + + let path = self.parse_optional_path_after_kind( + kind, + allow_turbofish, + allow_trailing_double_colon, + start_span, + )?; + if path.segments.is_empty() { + if path.kind != PathKind::Plain { + self.expected_identifier(); + } + None + } else { + Some(path) + } + } + + pub(super) fn parse_optional_path_after_kind( + &mut self, + kind: PathKind, + allow_turbofish: bool, + allow_trailing_double_colon: bool, + start_span: Span, + ) -> Option { + let path = self.parse_path_after_kind( + kind, + allow_turbofish, + allow_trailing_double_colon, + start_span, + ); + + if path.segments.is_empty() && path.kind == PathKind::Plain { + None + } else { + Some(path) + } + } + + /// Parses a path assuming the path's kind (plain, `crate::`, `super::`, etc.) + /// was already parsed. Note that this method always returns a Path, even if it + /// ends up being just `crate::` or an empty path. + pub(super) fn parse_path_after_kind( + &mut self, + kind: PathKind, + allow_turbofish: bool, + allow_trailing_double_colon: bool, + start_span: Span, + ) -> Path { + let mut segments = Vec::new(); + + if self.token.kind() == TokenKind::Ident { + loop { + let ident = self.eat_ident().unwrap(); + let span = ident.span(); + + let generics = if allow_turbofish + && self.at(Token::DoubleColon) + && self.next_is(Token::Less) + { + self.bump(); + self.parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) + } else { + None + }; + + segments.push(PathSegment { ident, generics, span }); + + if self.at(Token::DoubleColon) + && matches!(self.next_token.token(), Token::Ident(..)) + { + // Skip the double colons + self.bump(); + } else { + if allow_trailing_double_colon && self.eat_double_colon() { + self.expected_identifier(); + break; + } + + break; + } + } + } + + Path { segments, kind, span: self.span_since(start_span) } + } + + /// PathGenerics = GenericTypeArgs + pub(super) fn parse_path_generics( + &mut self, + on_named_arg_error: ParserErrorReason, + ) -> Option> { + if self.token.token() != &Token::Less { + return None; + }; -pub(super) fn maybe_empty_path() -> impl NoirParser { - path_no_turbofish().or(empty_path()) + let generics = self.parse_generic_type_args(); + for (name, _typ) in &generics.named_args { + self.push_error(on_named_arg_error.clone(), name.span()); + } + + Some(generics.ordered_args) + } + + /// PathKind + /// | 'crate' '::' + /// | 'dep' '::' + /// | 'super' '::' + /// | nothing + pub(super) fn parse_path_kind(&mut self) -> PathKind { + let kind = if self.eat_keyword(Keyword::Crate) { + PathKind::Crate + } else if self.eat_keyword(Keyword::Dep) { + PathKind::Dep + } else if self.eat_keyword(Keyword::Super) { + PathKind::Super + } else { + PathKind::Plain + }; + if kind != PathKind::Plain { + self.eat_or_error(Token::DoubleColon); + } + kind + } + + /// AsTraitPath = '<' Type 'as' PathNoTurbofish GenericTypeArgs '>' '::' identifier + pub(super) fn parse_as_trait_path(&mut self) -> Option { + if !self.eat_less() { + return None; + } + + let typ = self.parse_type_or_error(); + self.eat_keyword_or_error(Keyword::As); + let trait_path = self.parse_path_no_turbofish_or_error(); + let trait_generics = self.parse_generic_type_args(); + self.eat_or_error(Token::Greater); + self.eat_or_error(Token::DoubleColon); + let impl_item = if let Some(ident) = self.eat_ident() { + ident + } else { + self.expected_identifier(); + Ident::new(String::new(), self.span_at_previous_token_end()) + }; + + Some(AsTraitPath { typ, trait_path, trait_generics, impl_item }) + } } #[cfg(test)] -mod test { - use super::*; - use crate::parser::{ - parse_type, - parser::test_helpers::{parse_all_failing, parse_recover, parse_with}, +mod tests { + + use crate::{ + ast::{Path, PathKind}, + parser::{ + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + Parser, + }, }; + fn parse_path_no_errors(src: &str) -> Path { + let mut parser = Parser::for_str(src); + let path = parser.parse_path_or_error(); + expect_no_errors(&parser.errors); + path + } + #[test] - fn parse_path() { - let cases = vec![ - ("std", vec!["std"]), - ("std::hash", vec!["std", "hash"]), - ("std::hash::collections", vec!["std", "hash", "collections"]), - ("foo::bar", vec!["foo", "bar"]), - ("crate::std::hash", vec!["std", "hash"]), - ]; - - for (src, expected_segments) in cases { - let path: Path = parse_with(path(parse_type()), src).unwrap(); - for (segment, expected) in path.segments.into_iter().zip(expected_segments) { - assert_eq!(segment.ident.0.contents, expected); - } - } + fn parses_plain_one_segment() { + let src = "foo"; + let path = parse_path_no_errors(src); + assert_eq!(path.kind, PathKind::Plain); + assert_eq!(path.segments.len(), 1); + assert_eq!(path.segments[0].ident.to_string(), "foo"); + assert!(path.segments[0].generics.is_none()); + } - parse_all_failing(path(parse_type()), vec!["std::", "::std", "std::hash::", "foo::1"]); + #[test] + fn parses_plain_two_segments() { + let src = "foo::bar"; + let path = parse_path_no_errors(src); + assert_eq!(path.kind, PathKind::Plain); + assert_eq!(path.segments.len(), 2); + assert_eq!(path.segments[0].ident.to_string(), "foo"); + assert!(path.segments[0].generics.is_none()); + assert_eq!(path.segments[1].ident.to_string(), "bar"); + assert!(path.segments[1].generics.is_none()); } #[test] - fn parse_path_kinds() { - let cases = vec![ - ("std", PathKind::Plain), - ("hash::collections", PathKind::Plain), - ("crate::std::hash", PathKind::Crate), - ("super::foo", PathKind::Super), - ]; - - for (src, expected_path_kind) in cases { - let path = parse_with(path(parse_type()), src).unwrap(); - assert_eq!(path.kind, expected_path_kind); - } + fn parses_crate_two_segments() { + let src = "crate::foo::bar"; + let path = parse_path_no_errors(src); + assert_eq!(path.kind, PathKind::Crate); + assert_eq!(path.segments.len(), 2); + assert_eq!(path.segments[0].ident.to_string(), "foo"); + assert!(path.segments[0].generics.is_none()); + assert_eq!(path.segments[1].ident.to_string(), "bar"); + assert!(path.segments[1].generics.is_none()); + } - parse_all_failing( - path(parse_type()), - vec!["crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], - ); + #[test] + fn parses_super_two_segments() { + let src = "super::foo::bar"; + let path = parse_path_no_errors(src); + assert_eq!(path.kind, PathKind::Super); + assert_eq!(path.segments.len(), 2); + assert_eq!(path.segments[0].ident.to_string(), "foo"); + assert!(path.segments[0].generics.is_none()); + assert_eq!(path.segments[1].ident.to_string(), "bar"); + assert!(path.segments[1].generics.is_none()); } #[test] - fn parse_path_with_trailing_colons() { - let src = "foo::bar::"; + fn parses_dep_two_segments() { + let src = "dep::foo::bar"; + let path = parse_path_no_errors(src); + assert_eq!(path.kind, PathKind::Dep); + assert_eq!(path.segments.len(), 2); + assert_eq!(path.segments[0].ident.to_string(), "foo"); + assert!(path.segments[0].generics.is_none()); + assert_eq!(path.segments[1].ident.to_string(), "bar"); + assert!(path.segments[1].generics.is_none()); + } + + #[test] + fn parses_plain_one_segment_with_trailing_colons() { + let src = "foo::"; + let mut parser = Parser::for_str(src); + let path = parser.parse_path_or_error(); + assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(parser.errors.len(), 1); + assert_eq!(path.kind, PathKind::Plain); + assert_eq!(path.segments.len(), 1); + assert_eq!(path.segments[0].ident.to_string(), "foo"); + assert!(path.segments[0].generics.is_none()); + } - let (path, errors) = parse_recover(path_no_turbofish(), src); - let path = path.unwrap(); + #[test] + fn parses_with_turbofish() { + let src = "foo::::bar"; + let mut path = parse_path_no_errors(src); + assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 2); - assert_eq!(path.segments[0].ident.0.contents, "foo"); - assert_eq!(path.segments[1].ident.0.contents, "bar"); + assert_eq!(path.segments[0].ident.to_string(), "foo"); + + let generics = path.segments.remove(0).generics; + assert_eq!(generics.unwrap().len(), 2); + + let generics = path.segments.remove(0).generics; + assert!(generics.is_none()); + } + + #[test] + fn parses_path_stops_before_trailing_double_colon() { + let src = "foo::bar::"; + let mut parser = Parser::for_str(src); + let path = parser.parse_path_or_error(); + assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(parser.errors.len(), 1); + assert_eq!(path.to_string(), "foo::bar"); + } + + #[test] + fn parses_path_with_turbofish_stops_before_trailing_double_colon() { + let src = "foo::bar::<1>::"; + let mut parser = Parser::for_str(src); + let path = parser.parse_path_or_error(); + assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(parser.errors.len(), 1); + assert_eq!(path.to_string(), "foo::bar::<1>"); + } + + #[test] + fn errors_on_crate_double_colons() { + let src = " + crate:: + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let path = parser.parse_path(); + assert!(path.is_none()); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected an identifier after ::"); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected an identifier but found end of input"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs new file mode 100644 index 00000000000..a10fe18fd79 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -0,0 +1,372 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Ident, Path, Pattern}, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, + token::{Keyword, Token, TokenKind}, +}; + +use super::{ + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, + Parser, +}; + +pub(crate) enum PatternOrSelf { + Pattern(Pattern), + SelfPattern(SelfPattern), +} + +/// SelfPattern is guaranteed to be `self`, `&self` or `&mut self` without a colon following it. +pub(crate) struct SelfPattern { + pub(crate) reference: bool, + pub(crate) mutable: bool, +} + +impl<'a> Parser<'a> { + pub(crate) fn parse_pattern_or_error(&mut self) -> Pattern { + if let Some(pattern) = self.parse_pattern() { + return pattern; + } + + self.expected_label(ParsingRuleLabel::Pattern); + Pattern::Identifier(Ident::new(String::new(), self.span_at_previous_token_end())) + } + + /// Pattern + /// = 'mut' PatternNoMut + pub(crate) fn parse_pattern(&mut self) -> Option { + let start_span = self.current_token_span; + let mutable = self.eat_keyword(Keyword::Mut); + self.parse_pattern_after_modifiers(mutable, start_span) + } + + /// PatternOrSelf + /// = Pattern + /// | SelfPattern + pub(crate) fn parse_pattern_or_self(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.next_is_colon() && self.eat_self() { + return Some(PatternOrSelf::SelfPattern(SelfPattern { + reference: false, + mutable: false, + })); + } + + if self.eat_keyword(Keyword::Mut) { + if !self.next_is_colon() && self.eat_self() { + return Some(PatternOrSelf::SelfPattern(SelfPattern { + reference: false, + mutable: true, + })); + } else { + return Some(PatternOrSelf::Pattern( + self.parse_pattern_after_modifiers(true, start_span)?, + )); + } + } + + if self.at(Token::Ampersand) && self.next_is(Token::Keyword(Keyword::Mut)) { + self.bump(); + self.bump(); + if !self.next_is_colon() && self.eat_self() { + return Some(PatternOrSelf::SelfPattern(SelfPattern { + reference: true, + mutable: true, + })); + } else { + self.push_error( + ParserErrorReason::RefMutCanOnlyBeUsedWithSelf, + self.current_token_span, + ); + return Some(PatternOrSelf::Pattern( + self.parse_pattern_after_modifiers(true, start_span)?, + )); + } + } + + Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_span)?)) + } + + fn next_is_colon(&self) -> bool { + self.next_is(Token::Colon) + } + + pub(crate) fn parse_pattern_after_modifiers( + &mut self, + mutable: bool, + start_span: Span, + ) -> Option { + let pattern = self.parse_pattern_no_mut()?; + Some(if mutable { + Pattern::Mutable( + Box::new(pattern), + self.span_since(start_span), + false, // is synthesized + ) + } else { + pattern + }) + } + + /// PatternNoMut + /// = InternedPattern + /// | TuplePattern + /// | StructPattern + /// | IdentifierPattern + /// + /// IdentifierPattern = identifier + fn parse_pattern_no_mut(&mut self) -> Option { + let start_span = self.current_token_span; + + if let Some(pattern) = self.parse_interned_pattern() { + return Some(pattern); + } + + if let Some(pattern) = self.parse_tuple_pattern() { + return Some(pattern); + } + + let Some(mut path) = self.parse_path() else { + if self.at_built_in_type() { + self.push_error( + ParserErrorReason::ExpectedPatternButFoundType(self.token.token().clone()), + self.current_token_span, + ); + } + return None; + }; + + if self.eat_left_brace() { + return Some(self.parse_struct_pattern(path, start_span)); + } + + if !path.is_ident() { + self.push_error(ParserErrorReason::InvalidPattern, path.span); + + let ident = path.segments.pop().unwrap().ident; + return Some(Pattern::Identifier(ident)); + } + + let ident = path.segments.remove(0).ident; + Some(Pattern::Identifier(ident)) + } + + /// InternedPattern = interned_pattern + fn parse_interned_pattern(&mut self) -> Option { + let Some(token) = self.eat_kind(TokenKind::InternedPattern) else { + return None; + }; + + match token.into_token() { + Token::InternedPattern(pattern) => { + Some(Pattern::Interned(pattern, self.previous_token_span)) + } + _ => unreachable!(), + } + } + + /// TuplePattern = '(' PatternList? ')' + /// + /// PatternList = Pattern ( ',' Pattern )* ','? + fn parse_tuple_pattern(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.eat_left_paren() { + return None; + } + + let patterns = self.parse_many( + "tuple elements", + separated_by_comma_until_right_paren(), + Self::parse_tuple_pattern_element, + ); + + Some(Pattern::Tuple(patterns, self.span_since(start_span))) + } + + fn parse_tuple_pattern_element(&mut self) -> Option { + if let Some(pattern) = self.parse_pattern() { + Some(pattern) + } else { + self.expected_label(ParsingRuleLabel::Pattern); + None + } + } + + /// StructPattern = Path '{' StructPatternFields? '}' + /// + /// StructPatternFields = StructPatternField ( ',' StructPatternField )? ','? + /// + /// StructPatternField = identifier ( ':' Pattern )? + fn parse_struct_pattern(&mut self, path: Path, start_span: Span) -> Pattern { + let fields = self.parse_many( + "struct fields", + separated_by_comma_until_right_brace(), + Self::parse_struct_pattern_field, + ); + + Pattern::Struct(path, fields, self.span_since(start_span)) + } + + fn parse_struct_pattern_field(&mut self) -> Option<(Ident, Pattern)> { + let Some(ident) = self.eat_ident() else { + self.expected_identifier(); + return None; + }; + + Some(if self.eat_colon() { + (ident, self.parse_pattern_or_error()) + } else { + (ident.clone(), Pattern::Identifier(ident)) + }) + } + + fn at_built_in_type(&self) -> bool { + matches!( + self.token.token(), + Token::Bool(..) + | Token::IntType(..) + | Token::Keyword(Keyword::Bool) + | Token::Keyword(Keyword::CtString) + | Token::Keyword(Keyword::Expr) + | Token::Keyword(Keyword::Field) + | Token::Keyword(Keyword::FunctionDefinition) + | Token::Keyword(Keyword::Module) + | Token::Keyword(Keyword::Quoted) + | Token::Keyword(Keyword::StructDefinition) + | Token::Keyword(Keyword::TraitConstraint) + | Token::Keyword(Keyword::TraitDefinition) + | Token::Keyword(Keyword::TraitImpl) + | Token::Keyword(Keyword::TypedExpr) + | Token::Keyword(Keyword::TypeType) + | Token::Keyword(Keyword::UnresolvedType) + ) + } +} + +#[cfg(test)] +mod tests { + + use crate::{ + ast::Pattern, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + token::{Keyword, Token}, + }; + + fn parse_pattern_no_errors(src: &str) -> Pattern { + let mut parser = Parser::for_str(src); + let pattern = parser.parse_pattern_or_error(); + expect_no_errors(&parser.errors); + pattern + } + + #[test] + fn parses_identifier_pattern() { + let src = "foo"; + let pattern = parse_pattern_no_errors(src); + let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; + assert_eq!(ident.to_string(), "foo"); + } + + #[test] + fn parses_mutable_pattern() { + let src = "mut foo"; + let pattern = parse_pattern_no_errors(src); + let Pattern::Mutable(pattern, _, _) = pattern else { panic!("Expected a mutable pattern") }; + let pattern: &Pattern = &pattern; + let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; + assert_eq!(ident.to_string(), "foo"); + } + + #[test] + fn parses_tuple_pattern() { + let src = "(foo, bar)"; + let pattern = parse_pattern_no_errors(src); + let Pattern::Tuple(mut patterns, _) = pattern else { panic!("Expected a tuple pattern") }; + assert_eq!(patterns.len(), 2); + + let pattern = patterns.remove(0); + let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; + assert_eq!(ident.to_string(), "foo"); + + let pattern = patterns.remove(0); + let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; + assert_eq!(ident.to_string(), "bar"); + } + + #[test] + fn parses_unclosed_tuple_pattern() { + let src = "(foo,"; + let mut parser = Parser::for_str(src); + let pattern = parser.parse_pattern_or_error(); + assert_eq!(parser.errors.len(), 1); + let Pattern::Tuple(patterns, _) = pattern else { panic!("Expected a tuple pattern") }; + assert_eq!(patterns.len(), 1); + } + + #[test] + fn parses_struct_pattern_no_fields() { + let src = "foo::Bar {}"; + let mut parser = Parser::for_str(src); + let pattern = parser.parse_pattern_or_error(); + expect_no_errors(&parser.errors); + let Pattern::Struct(path, patterns, _) = pattern else { + panic!("Expected a struct pattern") + }; + assert_eq!(path.to_string(), "foo::Bar"); + assert!(patterns.is_empty()); + } + + #[test] + fn parses_struct_pattern() { + let src = "foo::Bar { x: one, y }"; + let pattern = parse_pattern_no_errors(src); + let Pattern::Struct(path, mut patterns, _) = pattern else { + panic!("Expected a struct pattern") + }; + assert_eq!(path.to_string(), "foo::Bar"); + assert_eq!(patterns.len(), 2); + + let (ident, pattern) = patterns.remove(0); + assert_eq!(ident.to_string(), "x"); + assert_eq!(pattern.to_string(), "one"); + + let (ident, pattern) = patterns.remove(0); + assert_eq!(ident.to_string(), "y"); + assert_eq!(pattern.to_string(), "y"); + } + + #[test] + fn parses_unclosed_struct_pattern() { + let src = "foo::Bar { x"; + let mut parser = Parser::for_str(src); + let pattern = parser.parse_pattern_or_error(); + assert_eq!(parser.errors.len(), 1); + let Pattern::Struct(path, _, _) = pattern else { panic!("Expected a struct pattern") }; + assert_eq!(path.to_string(), "foo::Bar"); + } + + #[test] + fn errors_on_reserved_type() { + let src = " + Field + ^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let pattern = parser.parse_pattern(); + assert!(pattern.is_none()); + + let reason = get_single_error_reason(&parser.errors, span); + assert!(matches!( + reason, + ParserErrorReason::ExpectedPatternButFoundType(Token::Keyword(Keyword::Field)) + )); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/primitives.rs b/compiler/noirc_frontend/src/parser/parser/primitives.rs deleted file mode 100644 index 5a040f23619..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ /dev/null @@ -1,167 +0,0 @@ -use chumsky::prelude::*; - -use crate::ast::{ - ExpressionKind, GenericTypeArgs, Ident, PathSegment, StatementKind, UnaryOp, UnresolvedType, -}; -use crate::parser::ParserErrorReason; -use crate::{ - parser::{labels::ParsingRuleLabel, ExprParser, NoirParser, ParserError}, - token::{Keyword, Token, TokenKind}, -}; - -use super::path::{path, path_no_turbofish}; -use super::types::required_generic_type_args; - -/// This parser always parses no input and fails -pub(super) fn nothing() -> impl NoirParser { - one_of([]).map(|_| unreachable!("parser should always error")) -} - -pub(super) fn keyword(keyword: Keyword) -> impl NoirParser { - just(Token::Keyword(keyword)) -} - -pub(super) fn token_kind(token_kind: TokenKind) -> impl NoirParser { - filter_map(move |span, found: Token| { - if found.kind() == token_kind { - Ok(found) - } else { - Err(ParserError::expected_label( - ParsingRuleLabel::TokenKind(token_kind.clone()), - found, - span, - )) - } - }) -} - -pub(super) fn path_segment<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - ident().then(turbofish(type_parser)).validate(|(ident, generics), span, emit| { - if generics.as_ref().map_or(false, |generics| !generics.named_args.is_empty()) { - let reason = ParserErrorReason::AssociatedTypesNotAllowedInPaths; - emit(ParserError::with_reason(reason, span)); - } - - let generics = generics.map(|generics| generics.ordered_args); - PathSegment { ident, generics, span } - }) -} - -pub(super) fn path_segment_no_turbofish() -> impl NoirParser { - ident().map(PathSegment::from) -} - -pub(super) fn ident() -> impl NoirParser { - token_kind(TokenKind::Ident).map_with_span(Ident::from_token) -} - -// Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier -// to parse nested generic types. For normal expressions however, it means we have to manually -// parse two greater-than tokens as a single right-shift here. -pub(super) fn right_shift_operator() -> impl NoirParser { - just(Token::Greater).then(just(Token::Greater)).to(Token::ShiftRight) -} - -pub(super) fn not

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Bang).ignore_then(term_parser).map(|rhs| ExpressionKind::prefix(UnaryOp::Not, rhs)) -} - -pub(super) fn negation

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Minus) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::Minus, rhs)) -} - -pub(super) fn mutable_reference

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Ampersand) - .ignore_then(keyword(Keyword::Mut)) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::MutableReference, rhs)) -} - -pub(super) fn dereference

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Star) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::Dereference { implicitly_added: false }, rhs)) -} - -pub(super) fn turbofish<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser> + 'a { - just(Token::DoubleColon).ignore_then(required_generic_type_args(type_parser)).or_not() -} - -pub(super) fn variable() -> impl NoirParser { - path(super::parse_type()).map(ExpressionKind::Variable) -} - -pub(super) fn variable_no_turbofish() -> impl NoirParser { - path_no_turbofish().map(ExpressionKind::Variable) -} - -pub(super) fn macro_quote_marker() -> impl NoirParser { - token_kind(TokenKind::UnquoteMarker).map(|token| match token { - Token::UnquoteMarker(expr_id) => ExpressionKind::Resolved(expr_id), - other => unreachable!("Non-unquote-marker parsed as an unquote marker: {other:?}"), - }) -} - -pub(super) fn interned_expr() -> impl NoirParser { - token_kind(TokenKind::InternedExpr).map(|token| match token { - Token::InternedExpr(id) => ExpressionKind::Interned(id), - _ => unreachable!("token_kind(InternedExpr) guarantees we parse an interned expr"), - }) -} - -pub(super) fn interned_statement() -> impl NoirParser { - token_kind(TokenKind::InternedStatement).map(|token| match token { - Token::InternedStatement(id) => StatementKind::Interned(id), - _ => { - unreachable!("token_kind(InternedStatement) guarantees we parse an interned statement") - } - }) -} - -// This rule is so that we can re-parse StatementKind::Expression and Semi in -// an expression position (ignoring the semicolon) if needed. -pub(super) fn interned_statement_expr() -> impl NoirParser { - token_kind(TokenKind::InternedStatement).map(|token| match token { - Token::InternedStatement(id) => ExpressionKind::InternedStatement(id), - _ => { - unreachable!("token_kind(InternedStatement) guarantees we parse an interned statement") - } - }) -} - -#[cfg(test)] -mod test { - use crate::parser::parser::{ - expression, expression_no_constructors, fresh_statement, term, test_helpers::*, - }; - - #[test] - fn parse_unary() { - parse_all( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], - ); - parse_all_failing( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["+hello", "/hello"], - ); - } -} diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs new file mode 100644 index 00000000000..d118be5d54a --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -0,0 +1,725 @@ +use noirc_errors::{Span, Spanned}; + +use crate::{ + ast::{ + AssignStatement, BinaryOp, BinaryOpKind, ConstrainKind, ConstrainStatement, Expression, + ExpressionKind, ForBounds, ForLoopStatement, ForRange, Ident, InfixExpression, LValue, + LetStatement, Statement, StatementKind, + }, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, + token::{Attribute, Keyword, Token, TokenKind}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_statement_or_error(&mut self) -> Statement { + if let Some((statement, (_token, _span))) = self.parse_statement() { + statement + } else { + self.expected_label(ParsingRuleLabel::Statement); + Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() } + } + } + + /// Statement = Attributes StatementKind ';'? + pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { + loop { + let attributes = self.parse_attributes(); + let start_span = self.current_token_span; + let kind = self.parse_statement_kind(attributes); + + let (semicolon_token, semicolon_span) = if self.at(Token::Semicolon) { + let token = self.token.clone(); + self.bump(); + let span = token.to_span(); + + (Some(token.into_token()), span) + } else { + (None, self.previous_token_span) + }; + + let span = self.span_since(start_span); + + if let Some(kind) = kind { + let statement = Statement { kind, span }; + return Some((statement, (semicolon_token, semicolon_span))); + } + + self.expected_label(ParsingRuleLabel::Statement); + + if semicolon_token.is_some() || self.at(Token::RightBrace) || self.at_eof() { + return None; + } else { + self.bump(); + } + } + } + + /// StatementKind + /// = BreakStatement + /// | ContinueStatement + /// | ReturnStatement + /// | LetStatement + /// | ConstrainStatement + /// | ComptimeStatement + /// | ForStatement + /// | IfStatement + /// | BlockStatement + /// | AssignStatement + /// | ExpressionStatement + /// + /// BreakStatement = 'break' + /// + /// ContinueStatement = 'continue' + /// + /// ReturnStatement = 'return' Expression? + /// + /// IfStatement = IfExpression + /// + /// BlockStatement = Block + /// + /// AssignStatement = Expression '=' Expression + /// + /// ExpressionStatement = Expression + fn parse_statement_kind( + &mut self, + attributes: Vec<(Attribute, Span)>, + ) -> Option { + let start_span = self.current_token_span; + + if let Some(token) = self.eat_kind(TokenKind::InternedStatement) { + match token.into_token() { + Token::InternedStatement(statement) => { + return Some(StatementKind::Interned(statement)) + } + _ => unreachable!(), + } + } + + if self.eat_keyword(Keyword::Break) { + return Some(StatementKind::Break); + } + + if self.eat_keyword(Keyword::Continue) { + return Some(StatementKind::Continue); + } + + if self.eat_keyword(Keyword::Return) { + self.parse_expression(); + self.push_error(ParserErrorReason::EarlyReturn, self.span_since(start_span)); + return Some(StatementKind::Error); + } + + if self.at_keyword(Keyword::Let) { + let let_statement = self.parse_let_statement(attributes)?; + return Some(StatementKind::Let(let_statement)); + } + + if let Some(constrain) = self.parse_constrain_statement() { + return Some(StatementKind::Constrain(constrain)); + } + + if self.at_keyword(Keyword::Comptime) { + return self.parse_comptime_statement(attributes); + } + + if let Some(for_loop) = self.parse_for() { + return Some(StatementKind::For(for_loop)); + } + + if let Some(kind) = self.parse_if_expr() { + return Some(StatementKind::Expression(Expression { + kind, + span: self.span_since(start_span), + })); + } + + if let Some(block) = self.parse_block() { + return Some(StatementKind::Expression(Expression { + kind: ExpressionKind::Block(block), + span: self.span_since(start_span), + })); + } + + if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { + match token.into_token() { + Token::InternedLValue(lvalue) => { + let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); + self.eat_or_error(Token::Assign); + let expression = self.parse_expression_or_error(); + return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); + } + _ => unreachable!(), + } + } + + let expression = self.parse_expression()?; + + if self.eat_assign() { + if let Some(lvalue) = LValue::from_expression(expression.clone()) { + let expression = self.parse_expression_or_error(); + return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); + } else { + self.push_error( + ParserErrorReason::InvalidLeftHandSideOfAssignment, + expression.span, + ); + } + } + + if let Some(operator) = self.next_is_op_assign() { + if let Some(lvalue) = LValue::from_expression(expression.clone()) { + // Desugar `a = b` to `a = a b`. This relies on the evaluation of `a` having no side effects, + // which is currently enforced by the restricted syntax of LValues. + let infix = InfixExpression { + lhs: expression, + operator, + rhs: self.parse_expression_or_error(), + }; + let expression = Expression::new( + ExpressionKind::Infix(Box::new(infix)), + self.span_since(start_span), + ); + return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); + } else { + self.push_error( + ParserErrorReason::InvalidLeftHandSideOfAssignment, + expression.span, + ); + } + } + + Some(StatementKind::Expression(expression)) + } + + fn next_is_op_assign(&mut self) -> Option { + let start_span = self.current_token_span; + let operator = if self.next_is(Token::Assign) { + match self.token.token() { + Token::Plus => Some(BinaryOpKind::Add), + Token::Minus => Some(BinaryOpKind::Subtract), + Token::Star => Some(BinaryOpKind::Multiply), + Token::Slash => Some(BinaryOpKind::Divide), + Token::Percent => Some(BinaryOpKind::Modulo), + Token::Ampersand => Some(BinaryOpKind::And), + Token::Caret => Some(BinaryOpKind::Xor), + Token::ShiftLeft => Some(BinaryOpKind::ShiftLeft), + Token::Pipe => Some(BinaryOpKind::Or), + _ => None, + } + } else if self.at(Token::Greater) && self.next_is(Token::GreaterEqual) { + // >>= + Some(BinaryOpKind::ShiftRight) + } else { + None + }; + + if let Some(operator) = operator { + self.bump(); + self.bump(); + Some(Spanned::from(self.span_since(start_span), operator)) + } else { + None + } + } + + /// ForStatement = 'for' identifier 'in' ForRange Block + fn parse_for(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.eat_keyword(Keyword::For) { + return None; + } + + let Some(identifier) = self.eat_ident() else { + self.expected_identifier(); + let identifier = Ident::default(); + return Some(self.empty_for_loop(identifier, start_span)); + }; + + if !self.eat_keyword(Keyword::In) { + self.expected_token(Token::Keyword(Keyword::In)); + return Some(self.empty_for_loop(identifier, start_span)); + } + + let range = self.parse_for_range(); + + let block_start_span = self.current_token_span; + let block = if let Some(block) = self.parse_block() { + Expression { + kind: ExpressionKind::Block(block), + span: self.span_since(block_start_span), + } + } else { + self.expected_token(Token::LeftBrace); + Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + }; + + Some(ForLoopStatement { identifier, range, block, span: self.span_since(start_span) }) + } + + /// ForRange + /// = ExpressionExceptConstructor + /// | ExpressionExceptConstructor '..' ExpressionExceptConstructor + fn parse_for_range(&mut self) -> ForRange { + let expr = self.parse_expression_except_constructor_or_error(); + + if self.eat(Token::DoubleDot) { + let end = self.parse_expression_except_constructor_or_error(); + ForRange::Range(ForBounds { start: expr, end, inclusive: false }) + } else if self.eat(Token::DoubleDotEqual) { + let end = self.parse_expression_except_constructor_or_error(); + ForRange::Range(ForBounds { start: expr, end, inclusive: true }) + } else { + ForRange::Array(expr) + } + } + + fn empty_for_loop(&mut self, identifier: Ident, start_span: Span) -> ForLoopStatement { + ForLoopStatement { + identifier, + range: ForRange::Array(Expression { + kind: ExpressionKind::Error, + span: Span::default(), + }), + block: Expression { kind: ExpressionKind::Error, span: Span::default() }, + span: self.span_since(start_span), + } + } + + /// ComptimeStatement + /// = ComptimeBlock + /// | ComptimeLet + /// | ComptimeFor + /// + /// ComptimeBlock = 'comptime' Block + /// + /// ComptimeLet = 'comptime' LetStatement + /// + /// ComptimeFor = 'comptime' ForStatement + fn parse_comptime_statement( + &mut self, + attributes: Vec<(Attribute, Span)>, + ) -> Option { + let start_span = self.current_token_span; + + if !self.eat_keyword(Keyword::Comptime) { + return None; + } + + if let Some(kind) = self.parse_comptime_statement_kind(attributes) { + return Some(StatementKind::Comptime(Box::new(Statement { + kind, + span: self.span_since(start_span), + }))); + } + + self.expected_one_of_tokens(&[ + Token::LeftBrace, + Token::Keyword(Keyword::Let), + Token::Keyword(Keyword::For), + ]); + + None + } + + fn parse_comptime_statement_kind( + &mut self, + attributes: Vec<(Attribute, Span)>, + ) -> Option { + let start_span = self.current_token_span; + + if let Some(block) = self.parse_block() { + return Some(StatementKind::Expression(Expression { + kind: ExpressionKind::Block(block), + span: self.span_since(start_span), + })); + } + + if let Some(let_statement) = self.parse_let_statement(attributes) { + return Some(StatementKind::Let(let_statement)); + } + + if let Some(for_loop) = self.parse_for() { + return Some(StatementKind::For(for_loop)); + } + + None + } + + /// LetStatement = 'let' pattern OptionalTypeAnnotation '=' Expression + fn parse_let_statement(&mut self, attributes: Vec<(Attribute, Span)>) -> Option { + if !self.eat_keyword(Keyword::Let) { + return None; + } + + let attributes = self.validate_secondary_attributes(attributes); + let pattern = self.parse_pattern_or_error(); + let r#type = self.parse_optional_type_annotation(); + let expression = if self.eat_assign() { + self.parse_expression_or_error() + } else { + self.expected_token(Token::Assign); + Expression { kind: ExpressionKind::Error, span: self.current_token_span } + }; + + Some(LetStatement { pattern, r#type, expression, attributes, comptime: false }) + } + + /// ConstrainStatement + /// = 'constrain' Expression + /// | 'assert' Arguments + /// | 'assert_eq' Arguments + fn parse_constrain_statement(&mut self) -> Option { + let start_span = self.current_token_span; + let Some(kind) = self.parse_constrain_kind() else { + return None; + }; + + Some(match kind { + ConstrainKind::Assert | ConstrainKind::AssertEq => { + let arguments = self.parse_arguments(); + if arguments.is_none() { + self.expected_token(Token::LeftParen); + } + let arguments = arguments.unwrap_or_default(); + + ConstrainStatement { kind, arguments, span: self.span_since(start_span) } + } + ConstrainKind::Constrain => { + self.push_error(ParserErrorReason::ConstrainDeprecated, self.previous_token_span); + + let expression = self.parse_expression_or_error(); + ConstrainStatement { + kind, + arguments: vec![expression], + span: self.span_since(start_span), + } + } + }) + } + + fn parse_constrain_kind(&mut self) -> Option { + if self.eat_keyword(Keyword::Assert) { + Some(ConstrainKind::Assert) + } else if self.eat_keyword(Keyword::AssertEq) { + Some(ConstrainKind::AssertEq) + } else if self.eat_keyword(Keyword::Constrain) { + Some(ConstrainKind::Constrain) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{ + ConstrainKind, ExpressionKind, ForRange, LValue, Statement, StatementKind, + UnresolvedTypeData, + }, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + }; + + fn parse_statement_no_errors(src: &str) -> Statement { + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement_or_error(); + expect_no_errors(&parser.errors); + statement + } + + #[test] + fn parses_break() { + let src = "break"; + let statement = parse_statement_no_errors(src); + assert!(matches!(statement.kind, StatementKind::Break)); + } + + #[test] + fn parses_continue() { + let src = "continue"; + let statement = parse_statement_no_errors(src); + assert!(matches!(statement.kind, StatementKind::Continue)); + } + + #[test] + fn parses_let_statement_no_type() { + let src = "let x = 1;"; + let statement = parse_statement_no_errors(src); + let StatementKind::Let(let_statement) = statement.kind else { + panic!("Expected let statement"); + }; + assert_eq!(let_statement.pattern.to_string(), "x"); + assert!(matches!(let_statement.r#type.typ, UnresolvedTypeData::Unspecified)); + assert_eq!(let_statement.expression.to_string(), "1"); + assert!(!let_statement.comptime); + } + + #[test] + fn parses_let_statement_with_type() { + let src = "let x: Field = 1;"; + let statement = parse_statement_no_errors(src); + let StatementKind::Let(let_statement) = statement.kind else { + panic!("Expected let statement"); + }; + assert_eq!(let_statement.pattern.to_string(), "x"); + assert_eq!(let_statement.r#type.to_string(), "Field"); + assert_eq!(let_statement.expression.to_string(), "1"); + assert!(!let_statement.comptime); + } + + #[test] + fn parses_assert() { + let src = "assert(true, \"good\")"; + let statement = parse_statement_no_errors(src); + let StatementKind::Constrain(constrain) = statement.kind else { + panic!("Expected constrain statement"); + }; + assert_eq!(constrain.kind, ConstrainKind::Assert); + assert_eq!(constrain.arguments.len(), 2); + } + + #[test] + fn parses_assert_eq() { + let src = "assert_eq(1, 2, \"bad\")"; + let statement = parse_statement_no_errors(src); + let StatementKind::Constrain(constrain) = statement.kind else { + panic!("Expected constrain statement"); + }; + assert_eq!(constrain.kind, ConstrainKind::AssertEq); + assert_eq!(constrain.arguments.len(), 3); + } + + #[test] + fn parses_constrain() { + let src = " + constrain 1 + ^^^^^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + let StatementKind::Constrain(constrain) = statement.kind else { + panic!("Expected constrain statement"); + }; + assert_eq!(constrain.kind, ConstrainKind::Constrain); + assert_eq!(constrain.arguments.len(), 1); + + let reason = get_single_error_reason(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ConstrainDeprecated)); + } + + #[test] + fn parses_comptime_block() { + let src = "comptime { 1 }"; + let statement = parse_statement_no_errors(src); + let StatementKind::Comptime(statement) = statement.kind else { + panic!("Expected comptime statement"); + }; + let StatementKind::Expression(expr) = statement.kind else { + panic!("Expected expression statement"); + }; + let ExpressionKind::Block(block) = expr.kind else { + panic!("Expected block expression"); + }; + assert_eq!(block.statements.len(), 1); + } + + #[test] + fn parses_comptime_let() { + let src = "comptime let x = 1;"; + let statement = parse_statement_no_errors(src); + let StatementKind::Comptime(statement) = statement.kind else { + panic!("Expected comptime statement"); + }; + let StatementKind::Let(..) = statement.kind else { + panic!("Expected let statement"); + }; + } + + #[test] + fn parses_for_array() { + let src = "for i in x { }"; + let statement = parse_statement_no_errors(src); + let StatementKind::For(for_loop) = statement.kind else { + panic!("Expected for loop"); + }; + assert_eq!(for_loop.identifier.to_string(), "i"); + let ForRange::Array(expr) = for_loop.range else { + panic!("Expected array"); + }; + assert_eq!(expr.to_string(), "x"); + } + + #[test] + fn parses_for_range() { + let src = "for i in 0..10 { }"; + let statement = parse_statement_no_errors(src); + let StatementKind::For(for_loop) = statement.kind else { + panic!("Expected for loop"); + }; + assert_eq!(for_loop.identifier.to_string(), "i"); + let ForRange::Range(bounds) = for_loop.range else { + panic!("Expected range"); + }; + assert_eq!(bounds.start.to_string(), "0"); + assert_eq!(bounds.end.to_string(), "10"); + assert!(!bounds.inclusive); + } + + #[test] + fn parses_for_range_inclusive() { + let src = "for i in 0..=10 { }"; + let statement = parse_statement_no_errors(src); + let StatementKind::For(for_loop) = statement.kind else { + panic!("Expected for loop"); + }; + assert_eq!(for_loop.identifier.to_string(), "i"); + let ForRange::Range(bounds) = for_loop.range else { + panic!("Expected range"); + }; + assert_eq!(bounds.start.to_string(), "0"); + assert_eq!(bounds.end.to_string(), "10"); + assert!(bounds.inclusive); + } + + #[test] + fn parses_comptime_for() { + let src = "comptime for i in x { }"; + let statement = parse_statement_no_errors(src); + let StatementKind::Comptime(statement) = statement.kind else { + panic!("Expected comptime"); + }; + let StatementKind::For(for_loop) = statement.kind else { + panic!("Expected for loop"); + }; + assert_eq!(for_loop.identifier.to_string(), "i"); + assert!(matches!(for_loop.range, ForRange::Array(..))); + } + + #[test] + fn parses_assignment() { + let src = "x = 1"; + let statement = parse_statement_no_errors(src); + let StatementKind::Assign(assign) = statement.kind else { + panic!("Expected assign"); + }; + let LValue::Ident(ident) = assign.lvalue else { + panic!("Expected ident"); + }; + assert_eq!(ident.to_string(), "x"); + assert_eq!(assign.expression.to_string(), "1"); + } + + #[test] + fn parses_assignment_with_parentheses() { + let src = "(x)[0] = 1"; + let statement = parse_statement_no_errors(src); + let StatementKind::Assign(..) = statement.kind else { + panic!("Expected assign"); + }; + } + + #[test] + fn parses_op_assignment() { + let src = "x += 1"; + let statement = parse_statement_no_errors(src); + let StatementKind::Assign(assign) = statement.kind else { + panic!("Expected assign"); + }; + assert_eq!(assign.to_string(), "x = (x + 1)"); + } + + #[test] + fn parses_op_assignment_with_shift_right() { + let src = "x >>= 1"; + let statement = parse_statement_no_errors(src); + let StatementKind::Assign(assign) = statement.kind else { + panic!("Expected assign"); + }; + assert_eq!(assign.to_string(), "x = (x >> 1)"); + } + + #[test] + fn parses_if_statement_followed_by_tuple() { + // This shouldn't be parsed as a call + let src = "{ if 1 { 2 } (3, 4) }"; + let statement = parse_statement_no_errors(src); + let StatementKind::Expression(expr) = statement.kind else { + panic!("Expected expr"); + }; + let ExpressionKind::Block(block) = expr.kind else { + panic!("Expected block"); + }; + assert_eq!(block.statements.len(), 2); + } + + #[test] + fn parses_block_followed_by_tuple() { + // This shouldn't be parsed as a call + let src = "{ { 2 } (3, 4) }"; + let statement = parse_statement_no_errors(src); + let StatementKind::Expression(expr) = statement.kind else { + panic!("Expected expr"); + }; + let ExpressionKind::Block(block) = expr.kind else { + panic!("Expected block"); + }; + assert_eq!(block.statements.len(), 2); + } + + #[test] + fn errors_on_return_statement() { + // This shouldn't be parsed as a call + let src = " + return 1 + ^^^^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(matches!(statement.kind, StatementKind::Error)); + let reason = get_single_error_reason(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::EarlyReturn)); + } + + #[test] + fn recovers_on_unknown_statement_followed_by_actual_statement() { + let src = " + ] let x = 1; + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(matches!(statement.kind, StatementKind::Let(..))); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a statement but found ]"); + } + + #[test] + fn recovers_on_unknown_statement_followed_by_semicolon() { + let src = " ] ;"; + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(statement.is_none()); + assert_eq!(parser.errors.len(), 2); + } + + #[test] + fn recovers_on_unknown_statement_followed_by_right_brace() { + let src = " ] }"; + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(statement.is_none()); + assert_eq!(parser.errors.len(), 2); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 729f276e9b8..6775cf35a78 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -1,84 +1,270 @@ -use chumsky::prelude::*; +use noirc_errors::Span; -use crate::ast::{Documented, NoirStruct, StructField}; -use crate::parser::parser::visibility::item_visibility; use crate::{ - parser::{ - parser::{ - attributes::{attributes, validate_secondary_attributes}, - function, parse_type, - primitives::{ident, keyword}, - }, - NoirParser, TopLevelStatementKind, - }, - token::{Keyword, Token}, + ast::{Documented, Ident, ItemVisibility, NoirStruct, StructField, UnresolvedGenerics}, + parser::ParserErrorReason, + token::{Attribute, SecondaryAttribute, Token}, }; -use super::doc_comments::outer_doc_comments; - -pub(super) fn struct_definition() -> impl NoirParser { - use self::Keyword::Struct; - use Token::*; - - let fields = struct_fields() - .delimited_by(just(LeftBrace), just(RightBrace)) - .recover_with(nested_delimiters( - LeftBrace, - RightBrace, - [(LeftParen, RightParen), (LeftBracket, RightBracket)], - |_| vec![], - )) - .or(just(Semicolon).to(Vec::new())); - - attributes() - .then(item_visibility()) - .then_ignore(keyword(Struct)) - .then(ident()) - .then(function::generics()) - .then(fields) - .validate(|((((attributes, visibility), name), generics), fields), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::Struct(NoirStruct { - name, +use super::{parse_many::separated_by_comma_until_right_brace, Parser}; + +impl<'a> Parser<'a> { + /// Struct = 'struct' identifier Generics '{' StructField* '}' + /// + /// StructField = OuterDocComments identifier ':' Type + pub(crate) fn parse_struct( + &mut self, + attributes: Vec<(Attribute, Span)>, + visibility: ItemVisibility, + start_span: Span, + ) -> NoirStruct { + let attributes = self.validate_secondary_attributes(attributes); + + let Some(name) = self.eat_ident() else { + self.expected_identifier(); + return self.empty_struct( + Ident::default(), attributes, visibility, - generics, - fields, - span, - }) - }) -} + Vec::new(), + start_span, + ); + }; + + let generics = self.parse_generics(); + + if self.eat_semicolons() { + return self.empty_struct(name, attributes, visibility, generics, start_span); + } + + if !self.eat_left_brace() { + self.expected_token(Token::LeftBrace); + return self.empty_struct(name, attributes, visibility, generics, start_span); + } + + let fields = self.parse_many( + "struct fields", + separated_by_comma_until_right_brace(), + Self::parse_struct_field, + ); + + NoirStruct { + name, + attributes, + visibility, + generics, + fields, + span: self.span_since(start_span), + } + } + + fn parse_struct_field(&mut self) -> Option> { + let mut doc_comments; + let name; + + // Loop until we find an identifier, skipping anything that's not one + loop { + let doc_comments_start_span = self.current_token_span; + doc_comments = self.parse_outer_doc_comments(); + + if let Some(ident) = self.eat_ident() { + name = ident; + break; + } -fn struct_fields() -> impl NoirParser>> { - let field = ident().then_ignore(just(Token::Colon)).then(parse_type()); - let field = outer_doc_comments().then(field).map(|(doc_comments, (name, typ))| { - Documented::new(StructField { name, typ }, doc_comments) - }); - field.separated_by(just(Token::Comma)).allow_trailing() + if !doc_comments.is_empty() { + self.push_error( + ParserErrorReason::DocCommentDoesNotDocumentAnything, + self.span_since(doc_comments_start_span), + ); + } + + // Though we do have to stop at EOF + if self.at_eof() { + self.expected_token(Token::RightBrace); + return None; + } + + // Or if we find a right brace + if self.at(Token::RightBrace) { + return None; + } + + self.expected_identifier(); + self.bump(); + } + + self.eat_or_error(Token::Colon); + + let typ = self.parse_type_or_error(); + Some(Documented::new(StructField { name, typ }, doc_comments)) + } + + fn empty_struct( + &self, + name: Ident, + attributes: Vec, + visibility: ItemVisibility, + generics: UnresolvedGenerics, + start_span: Span, + ) -> NoirStruct { + NoirStruct { + name, + attributes, + visibility, + generics, + fields: Vec::new(), + span: self.span_since(start_span), + } + } } #[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; +mod tests { + use crate::{ + ast::{IntegerBitSize, NoirStruct, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parser::{ + parser::{ + parse_program, + tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, + }, + ItemKind, ParserErrorReason, + }, + }; + + fn parse_struct_no_errors(src: &str) -> NoirStruct { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Struct(noir_struct) = item.kind else { + panic!("Expected struct"); + }; + noir_struct + } #[test] - fn parse_structs() { - let cases = vec![ - "struct Foo;", - "struct Foo { }", - "struct Bar { ident: Field, }", - "struct Baz { ident: Field, other: Field }", - "#[attribute] struct Baz { ident: Field, other: Field }", - ]; - parse_all(struct_definition(), cases); - - let failing = vec![ - "struct { }", - "struct Foo { bar: pub Field }", - "struct Foo { bar: pub Field }", - "#[oracle(some)] struct Foo { bar: Field }", - ]; - parse_all_failing(struct_definition(), failing); + fn parse_empty_struct() { + let src = "struct Foo {}"; + let noir_struct = parse_struct_no_errors(src); + assert_eq!("Foo", noir_struct.name.to_string()); + assert!(noir_struct.fields.is_empty()); + assert!(noir_struct.generics.is_empty()); + } + + #[test] + fn parse_empty_struct_followed_by_semicolon() { + let src = "struct Foo;"; + let noir_struct = parse_struct_no_errors(src); + assert_eq!("Foo", noir_struct.name.to_string()); + assert!(noir_struct.fields.is_empty()); + assert!(noir_struct.generics.is_empty()); + } + + #[test] + fn parse_empty_struct_with_generics() { + let src = "struct Foo {}"; + let mut noir_struct = parse_struct_no_errors(src); + assert_eq!("Foo", noir_struct.name.to_string()); + assert!(noir_struct.fields.is_empty()); + assert_eq!(noir_struct.generics.len(), 2); + + let generic = noir_struct.generics.remove(0); + let UnresolvedGeneric::Variable(ident) = generic else { + panic!("Expected generic variable"); + }; + assert_eq!("A", ident.to_string()); + + let generic = noir_struct.generics.remove(0); + let UnresolvedGeneric::Numeric { ident, typ } = generic else { + panic!("Expected generic numeric"); + }; + assert_eq!("B", ident.to_string()); + assert_eq!( + typ.typ, + UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) + ); + } + + #[test] + fn parse_struct_with_fields() { + let src = "struct Foo { x: i32, y: Field }"; + let mut noir_struct = parse_struct_no_errors(src); + assert_eq!("Foo", noir_struct.name.to_string()); + assert_eq!(noir_struct.fields.len(), 2); + + let field = noir_struct.fields.remove(0).item; + assert_eq!("x", field.name.to_string()); + assert!(matches!( + field.typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) + )); + + let field = noir_struct.fields.remove(0).item; + assert_eq!("y", field.name.to_string()); + assert!(matches!(field.typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parse_empty_struct_with_doc_comments() { + let src = "/// Hello\nstruct Foo {}"; + let (module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + assert_eq!(item.doc_comments.len(), 1); + let ItemKind::Struct(noir_struct) = &item.kind else { + panic!("Expected struct"); + }; + assert_eq!("Foo", noir_struct.name.to_string()); + } + + #[test] + fn parse_unclosed_struct() { + let src = "struct Foo {"; + let (module, errors) = parse_program(src); + assert_eq!(errors.len(), 1); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Struct(noir_struct) = &item.kind else { + panic!("Expected struct"); + }; + assert_eq!("Foo", noir_struct.name.to_string()); + } + + #[test] + fn parse_error_no_function_attributes_allowed_on_struct() { + let src = " + #[test] struct Foo {} + ^^^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (_, errors) = parse_program(&src); + let reason = get_single_error_reason(&errors, span); + assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnStruct)); + } + + #[test] + fn recovers_on_non_field() { + let src = " + struct Foo { 42 x: i32 } + ^^ + "; + let (src, span) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Struct(noir_struct) = &item.kind else { + panic!("Expected struct"); + }; + assert_eq!("Foo", noir_struct.name.to_string()); + assert_eq!(noir_struct.fields.len(), 1); + + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected an identifier but found 42"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/test_helpers.rs b/compiler/noirc_frontend/src/parser/parser/test_helpers.rs deleted file mode 100644 index 83c1e148f0e..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/test_helpers.rs +++ /dev/null @@ -1,122 +0,0 @@ -use chumsky::primitive::just; -use chumsky::Parser; -use iter_extended::vecmap; -use noirc_errors::CustomDiagnostic; - -use crate::{ - lexer::Lexer, - parser::{force, NoirParser}, - token::Token, -}; - -pub(crate) fn parse_with(parser: P, program: &str) -> Result> -where - P: NoirParser, -{ - let (tokens, lexer_errors) = Lexer::lex(program); - if !lexer_errors.is_empty() { - return Err(vecmap(&lexer_errors, Into::into)); - } - parser.then_ignore(just(Token::EOF)).parse(tokens).map_err(|errors| vecmap(&errors, Into::into)) -} - -pub(crate) fn parse_recover(parser: P, program: &str) -> (Option, Vec) -where - P: NoirParser, -{ - let (tokens, lexer_errors) = Lexer::lex(program); - let (opt, errs) = parser.then_ignore(force(just(Token::EOF))).parse_recovery(tokens); - - let mut errors = vecmap(&lexer_errors, Into::into); - errors.extend(errs.iter().map(Into::into)); - - (opt, errors) -} - -pub(crate) fn parse_all(parser: P, programs: Vec<&str>) -> Vec -where - P: NoirParser, -{ - vecmap(programs, move |program| { - let message = format!("Failed to parse:\n{program}"); - let (op_t, diagnostics) = parse_recover(&parser, program); - diagnostics.iter().for_each(|diagnostic| { - if diagnostic.is_error() { - panic!("{} with error {}", &message, diagnostic); - } - }); - op_t.expect(&message) - }) -} - -pub(crate) fn parse_all_failing(parser: P, programs: Vec<&str>) -> Vec -where - P: NoirParser, - T: std::fmt::Display, -{ - programs - .into_iter() - .flat_map(|program| match parse_with(&parser, program) { - Ok(expr) => { - unreachable!( - "Expected this input to fail:\n{}\nYet it successfully parsed as:\n{}", - program, expr - ) - } - Err(diagnostics) => { - if diagnostics.iter().all(|diagnostic: &CustomDiagnostic| diagnostic.is_warning()) { - unreachable!( - "Expected at least one error when parsing:\n{}\nYet it successfully parsed without errors:\n", - program - ) - }; - diagnostics - } - }) - .collect() -} - -#[derive(Copy, Clone)] -pub(crate) struct Case { - pub(crate) source: &'static str, - pub(crate) errors: usize, - pub(crate) expect: &'static str, -} - -pub(crate) fn check_cases_with_errors(cases: &[Case], parser: P) -where - P: NoirParser + Clone, - T: std::fmt::Display, -{ - let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); - - let results = vecmap(cases, |&case| { - let (opt, errors) = parse_recover(parser.clone(), case.source); - let actual = opt.map(|ast| ast.to_string()); - let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; - - let result = ((errors.len(), actual.clone()), (case.errors, case.expect.to_string())); - if result.0 != result.1 { - let num_errors = errors.len(); - let shown_errors = show_errors(errors); - eprintln!( - concat!( - "\nExpected {expected_errors} error(s) and got {num_errors}:", - "\n\n{shown_errors}", - "\n\nFrom input: {src}", - "\nExpected AST: {expected_result}", - "\nActual AST: {actual}\n", - ), - expected_errors = case.errors, - num_errors = num_errors, - shown_errors = shown_errors, - src = case.source, - expected_result = case.expect, - actual = actual, - ); - } - result - }); - - assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); -} diff --git a/compiler/noirc_frontend/src/parser/parser/tests.rs b/compiler/noirc_frontend/src/parser/parser/tests.rs new file mode 100644 index 00000000000..ea8b1fc638d --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/tests.rs @@ -0,0 +1,55 @@ +#![cfg(test)] + +use noirc_errors::Span; + +use crate::parser::{ParserError, ParserErrorReason}; + +pub(super) fn get_source_with_error_span(src: &str) -> (String, Span) { + let mut lines: Vec<&str> = src.trim_end().lines().collect(); + let squiggles_line = lines.pop().expect("Expected at least two lines in src (the last one should have squiggles for the error location)"); + let squiggle_index = squiggles_line + .chars() + .position(|char| char == '^') + .expect("Expected at least one `^` character in the last line of the src"); + let squiggle_length = squiggles_line.len() - squiggle_index; + let last_line = lines.last().expect("Expected at least two lines in src"); + let src = lines.join("\n"); + let span_start = src.len() - last_line.len() + squiggle_index; + let span_end = span_start + squiggle_length; + let span = Span::from(span_start as u32..span_end as u32); + (src, span) +} + +pub(super) fn get_single_error(errors: &[ParserError], expected_span: Span) -> &ParserError { + if errors.is_empty() { + panic!("Expected an error, found none"); + } + + if errors.len() > 1 { + for error in errors { + println!("{}", error); + } + panic!("Expected one error, found {} errors (printed above)", errors.len()); + } + + assert_eq!(errors[0].span(), expected_span); + &errors[0] +} + +pub(super) fn get_single_error_reason( + errors: &[ParserError], + expected_span: Span, +) -> &ParserErrorReason { + get_single_error(errors, expected_span).reason().unwrap() +} + +pub(super) fn expect_no_errors(errors: &[ParserError]) { + if errors.is_empty() { + return; + } + + for error in errors { + println!("{}", error); + } + panic!("Expected no errors, found {} errors (printed above)", errors.len()); +} diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 78453d7f7a2..3bae152e75f 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -1,342 +1,295 @@ -use chumsky::prelude::*; - -use super::attributes::{attributes, validate_secondary_attributes}; -use super::doc_comments::outer_doc_comments; -use super::function::{function_modifiers, function_return_type}; -use super::path::path_no_turbofish; -use super::visibility::item_visibility; -use super::{ - block, expression, fresh_statement, function, function_declaration_parameters, let_statement, -}; +use noirc_errors::Span; -use crate::ast::{ - Documented, Expression, ItemVisibility, NoirTrait, NoirTraitImpl, Pattern, TraitBound, - TraitImplItem, TraitImplItemKind, TraitItem, UnresolvedTraitConstraint, UnresolvedType, -}; -use crate::parser::spanned; +use crate::ast::{Documented, ItemVisibility, NoirTrait, Pattern, TraitItem, UnresolvedType}; use crate::{ - parser::{ - ignore_then_commit, parenthesized, parser::primitives::keyword, NoirParser, ParserError, - ParserErrorReason, TopLevelStatementKind, - }, - token::{Keyword, Token}, + ast::{Ident, UnresolvedTypeData}, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, + token::{Attribute, Keyword, SecondaryAttribute, Token}, }; -use super::{generic_type_args, parse_type, primitives::ident}; - -pub(super) fn trait_definition() -> impl NoirParser { - let trait_body_or_error = just(Token::LeftBrace) - .ignore_then(trait_body()) - .then_ignore(just(Token::RightBrace)) - .or_not() - .validate(|items, span, emit| { - if let Some(items) = items { - items - } else { - emit(ParserError::with_reason( - ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitName, - span, - )); - vec![] - } - }); - - attributes() - .then(item_visibility()) - .then_ignore(keyword(Keyword::Trait)) - .then(ident()) - .then(function::generics()) - .then(where_clause()) - .then(trait_body_or_error) - .validate( - |(((((attributes, visibility), name), generics), where_clause), items), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::Trait(NoirTrait { - name, - generics, - where_clause, - span, - items, - attributes, - visibility, - }) - }, - ) -} - -fn trait_body() -> impl NoirParser>> { - let item = - trait_function_declaration().or(trait_type_declaration()).or(trait_constant_declaration()); - outer_doc_comments() - .then(item) - .map(|(doc_comments, item)| Documented::new(item, doc_comments)) - .repeated() -} +use super::parse_many::without_separator; +use super::Parser; + +impl<'a> Parser<'a> { + /// Trait = 'trait' identifier Generics WhereClause TraitBody + pub(crate) fn parse_trait( + &mut self, + attributes: Vec<(Attribute, Span)>, + visibility: ItemVisibility, + start_span: Span, + ) -> NoirTrait { + let attributes = self.validate_secondary_attributes(attributes); + + let Some(name) = self.eat_ident() else { + self.expected_identifier(); + return empty_trait(attributes, visibility, self.span_since(start_span)); + }; -fn optional_default_value() -> impl NoirParser> { - ignore_then_commit(just(Token::Assign), expression()).or_not() -} + let generics = self.parse_generics(); + let where_clause = self.parse_where_clause(); + let items = self.parse_trait_body(); + + NoirTrait { + name, + generics, + where_clause, + span: self.span_since(start_span), + items, + attributes, + visibility, + } + } -fn trait_constant_declaration() -> impl NoirParser { - keyword(Keyword::Let) - .ignore_then(ident()) - .then_ignore(just(Token::Colon)) - .then(parse_type()) - .then(optional_default_value()) - .then_ignore(just(Token::Semicolon)) - .map(|((name, typ), default_value)| TraitItem::Constant { name, typ, default_value }) -} + /// TraitBody = '{' ( OuterDocComments TraitItem )* '}' + fn parse_trait_body(&mut self) -> Vec> { + if !self.eat_left_brace() { + self.expected_token(Token::LeftBrace); + return Vec::new(); + } -/// trait_function_declaration: 'fn' ident generics '(' declaration_parameters ')' function_return_type -fn trait_function_declaration() -> impl NoirParser { - let trait_function_body_or_semicolon = - block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); - - let trait_function_body_or_semicolon_or_error = - trait_function_body_or_semicolon.or_not().validate(|body, span, emit| { - if let Some(body) = body { - body - } else { - emit(ParserError::with_reason( - ParserErrorReason::ExpectedLeftBraceOrArrowAfterFunctionParameters, - span, - )); - None - } - }); - - function_modifiers() - .then_ignore(keyword(Keyword::Fn)) - .then(ident()) - .then(function::generics()) - .then(parenthesized(function_declaration_parameters())) - .then(function_return_type().map(|(_, typ)| typ)) - .then(where_clause()) - .then(trait_function_body_or_semicolon_or_error) - .map( - |((((((modifiers, name), generics), parameters), return_type), where_clause), body)| { - TraitItem::Function { - name, - generics, - parameters, - return_type, - where_clause, - body, - is_unconstrained: modifiers.0, - visibility: modifiers.1, - is_comptime: modifiers.2, - } - }, + self.parse_many( + "trait items", + without_separator().until(Token::RightBrace), + Self::parse_trait_item_in_list, ) -} + } -/// trait_type_declaration: 'type' ident generics -fn trait_type_declaration() -> impl NoirParser { - keyword(Keyword::Type) - .ignore_then(ident()) - .then_ignore(just(Token::Semicolon)) - .map(|name| TraitItem::Type { name }) -} + fn parse_trait_item_in_list(&mut self) -> Option> { + self.parse_item_in_list(ParsingRuleLabel::TraitItem, |parser| { + let doc_comments = parser.parse_outer_doc_comments(); + parser.parse_trait_item().map(|item| Documented::new(item, doc_comments)) + }) + } -/// Parses a trait implementation, implementing a particular trait for a type. -/// This has a similar syntax to `implementation`, but the `for type` clause is required, -/// and an optional `where` clause is also useable. -/// -/// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' -pub(super) fn trait_implementation() -> impl NoirParser { - let body_or_error = - just(Token::LeftBrace) - .ignore_then(trait_implementation_body()) - .then_ignore(just(Token::RightBrace)) - .or_not() - .validate(|items, span, emit| { - if let Some(items) = items { - items - } else { - emit(ParserError::with_reason( - ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, - span, - )); + /// TraitItem + /// = TraitType + /// | TraitConstant + /// | TraitFunction + fn parse_trait_item(&mut self) -> Option { + if let Some(item) = self.parse_trait_type() { + return Some(item); + } - vec![] - } - }); - - keyword(Keyword::Impl) - .ignore_then(function::generics()) - .then(path_no_turbofish()) - .then(generic_type_args(parse_type())) - .then_ignore(keyword(Keyword::For)) - .then(parse_type()) - .then(where_clause()) - .then(body_or_error) - .map(|args| { - let (((other_args, object_type), where_clause), items) = args; - let ((impl_generics, trait_name), trait_generics) = other_args; - TopLevelStatementKind::TraitImpl(NoirTraitImpl { - impl_generics, - trait_name, - trait_generics, - object_type, - items, - where_clause, - }) - }) -} + if let Some(item) = self.parse_trait_constant() { + return Some(item); + } -fn trait_implementation_body() -> impl NoirParser>> { - let function = function::function_definition(true).validate(|mut f, span, emit| { - if f.def().visibility != ItemVisibility::Private { - emit(ParserError::with_reason(ParserErrorReason::TraitImplVisibilityIgnored, span)); + if let Some(item) = self.parse_trait_function() { + return Some(item); } - // Trait impl functions are always public - f.def_mut().visibility = ItemVisibility::Public; - TraitImplItemKind::Function(f) - }); - - let alias = keyword(Keyword::Type) - .ignore_then(ident()) - .then_ignore(just(Token::Assign)) - .then(parse_type()) - .then_ignore(just(Token::Semicolon)) - .map(|(name, alias)| TraitImplItemKind::Type { name, alias }); - - let let_statement = let_statement(expression()).then_ignore(just(Token::Semicolon)).try_map( - |((pattern, typ), expr), span| match pattern { - Pattern::Identifier(ident) => Ok(TraitImplItemKind::Constant(ident, typ, expr)), - _ => Err(ParserError::with_reason( - ParserErrorReason::PatternInTraitFunctionParameter, - span, - )), - }, - ); - let item = choice((function, alias, let_statement)); - outer_doc_comments() - .then(spanned(item).map(|(kind, span)| TraitImplItem { kind, span })) - .map(|(doc_comments, item)| Documented::new(item, doc_comments)) - .repeated() -} + None + } -pub(super) fn where_clause() -> impl NoirParser> { - struct MultiTraitConstraint { - typ: UnresolvedType, - trait_bounds: Vec, + /// TraitType = 'type' identifier ';' + fn parse_trait_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::Type) { + return None; + } + + let name = match self.eat_ident() { + Some(name) => name, + None => { + self.expected_identifier(); + Ident::default() + } + }; + + self.eat_semicolons(); + + Some(TraitItem::Type { name }) } - let constraints = parse_type() - .then_ignore(just(Token::Colon)) - .then(trait_bounds()) - .map(|(typ, trait_bounds)| MultiTraitConstraint { typ, trait_bounds }); - - keyword(Keyword::Where) - .ignore_then(constraints.separated_by(just(Token::Comma)).allow_trailing()) - .or_not() - .map(|option| option.unwrap_or_default()) - .map(|x: Vec| { - let mut result: Vec = Vec::new(); - for constraint in x { - for bound in constraint.trait_bounds { - result.push(UnresolvedTraitConstraint { - typ: constraint.typ.clone(), - trait_bound: bound, - }); - } + /// TraitConstant = 'let' identifier ':' Type ( '=' Expression ) ';' + fn parse_trait_constant(&mut self) -> Option { + if !self.eat_keyword(Keyword::Let) { + return None; + } + + let name = match self.eat_ident() { + Some(name) => name, + None => { + self.expected_identifier(); + Ident::default() } - result - }) -} + }; -fn trait_bounds() -> impl NoirParser> { - trait_bound().separated_by(just(Token::Plus)).at_least(1).allow_trailing() -} + let typ = if self.eat_colon() { + self.parse_type_or_error() + } else { + self.expected_token(Token::Colon); + UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + }; -pub fn trait_bound() -> impl NoirParser { - path_no_turbofish().then(generic_type_args(parse_type())).map(|(trait_path, trait_generics)| { - TraitBound { trait_path, trait_generics, trait_id: None } - }) -} + let default_value = + if self.eat_assign() { Some(self.parse_expression_or_error()) } else { None }; -#[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; + self.eat_semicolons(); - #[test] - fn parse_trait() { - parse_all( - trait_definition(), - vec![ - // Empty traits are legal in Rust and sometimes used as a way to whitelist certain types - // for a particular operation. Also known as `tag` or `marker` traits: - // https://stackoverflow.com/questions/71895489/what-is-the-purpose-of-defining-empty-impl-in-rust - "trait Empty {}", - "trait TraitWithDefaultBody { fn foo(self) {} }", - "trait TraitAcceptingMutableRef { fn foo(&mut self); }", - "trait TraitWithTypeBoundOperation { fn identity() -> Self; }", - "trait TraitWithAssociatedType { type Element; fn item(self, index: Field) -> Self::Element; }", - "trait TraitWithAssociatedConstant { let Size: Field; }", - "trait TraitWithAssociatedConstantWithDefaultValue { let Size: Field = 10; }", - "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", - "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", - "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { let Size: Field; fn zero() -> Self; }", - "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait, { let Size: Field; fn zero() -> Self; }", - ], + Some(TraitItem::Constant { name, typ, default_value }) + } + + /// TraitFunction = Modifiers Function + fn parse_trait_function(&mut self) -> Option { + let modifiers = self.parse_modifiers( + false, // allow mut ); - parse_all_failing( - trait_definition(), - vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], + if !self.eat_keyword(Keyword::Fn) { + self.modifiers_not_followed_by_an_item(modifiers); + return None; + } + + let function = self.parse_function_definition_with_optional_body( + true, // allow optional body + true, // allow self ); - } - #[test] - fn parse_recover_function_without_left_brace_or_semicolon() { - let src = "fn foo(x: i32)"; + let parameters = function + .parameters + .into_iter() + .filter_map(|param| { + if let Pattern::Identifier(ident) = param.pattern { + Some((ident, param.typ)) + } else { + self.push_error(ParserErrorReason::InvalidPattern, param.pattern.span()); + None + } + }) + .collect(); + + Some(TraitItem::Function { + is_unconstrained: modifiers.unconstrained.is_some(), + visibility: modifiers.visibility, + is_comptime: modifiers.comptime.is_some(), + name: function.name, + generics: function.generics, + parameters, + return_type: function.return_type, + where_clause: function.where_clause, + body: function.body, + }) + } +} - let (trait_item, errors) = parse_recover(trait_function_declaration(), src); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected { or -> after function parameters"); +fn empty_trait( + attributes: Vec, + visibility: ItemVisibility, + span: Span, +) -> NoirTrait { + NoirTrait { + name: Ident::default(), + generics: Vec::new(), + where_clause: Vec::new(), + span, + items: Vec::new(), + attributes, + visibility, + } +} - let Some(TraitItem::Function { name, parameters, body, .. }) = trait_item else { - panic!("Expected to parser trait item as function"); +#[cfg(test)] +mod tests { + use crate::{ + ast::{NoirTrait, TraitItem}, + parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }, + }; + + fn parse_trait_no_errors(src: &str) -> NoirTrait { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Trait(noir_trait) = item.kind else { + panic!("Expected trait"); }; + noir_trait + } - assert_eq!(name.to_string(), "foo"); - assert_eq!(parameters.len(), 1); - assert!(body.is_none()); + #[test] + fn parse_empty_trait() { + let src = "trait Foo {}"; + let noir_trait = parse_trait_no_errors(src); + assert_eq!(noir_trait.name.to_string(), "Foo"); + assert!(noir_trait.generics.is_empty()); + assert!(noir_trait.where_clause.is_empty()); + assert!(noir_trait.items.is_empty()); } #[test] - fn parse_recover_trait_without_body() { - let src = "trait Foo"; + fn parse_trait_with_generics() { + let src = "trait Foo {}"; + let noir_trait = parse_trait_no_errors(src); + assert_eq!(noir_trait.name.to_string(), "Foo"); + assert_eq!(noir_trait.generics.len(), 2); + assert!(noir_trait.where_clause.is_empty()); + assert!(noir_trait.items.is_empty()); + } - let (top_level_statement, errors) = parse_recover(trait_definition(), src); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected <, where or { after trait name"); + #[test] + fn parse_trait_with_where_clause() { + let src = "trait Foo where A: Z {}"; + let noir_trait = parse_trait_no_errors(src); + assert_eq!(noir_trait.name.to_string(), "Foo"); + assert_eq!(noir_trait.generics.len(), 2); + assert_eq!(noir_trait.where_clause.len(), 1); + assert!(noir_trait.items.is_empty()); + } - let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatementKind::Trait(trait_) = top_level_statement else { - panic!("Expected to parse a trait"); + #[test] + fn parse_trait_with_type() { + let src = "trait Foo { type Elem; }"; + let mut noir_trait = parse_trait_no_errors(src); + assert_eq!(noir_trait.items.len(), 1); + + let item = noir_trait.items.remove(0).item; + let TraitItem::Type { name } = item else { + panic!("Expected type"); }; - - assert_eq!(trait_.name.to_string(), "Foo"); - assert!(trait_.items.is_empty()); + assert_eq!(name.to_string(), "Elem"); } #[test] - fn parse_recover_trait_impl_without_body() { - let src = "impl Foo for Bar"; - - let (top_level_statement, errors) = parse_recover(trait_implementation(), src); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected <, where or { after trait impl for type"); + fn parse_trait_with_constant() { + let src = "trait Foo { let x: Field = 1; }"; + let mut noir_trait = parse_trait_no_errors(src); + assert_eq!(noir_trait.items.len(), 1); + + let item = noir_trait.items.remove(0).item; + let TraitItem::Constant { name, typ, default_value } = item else { + panic!("Expected constant"); + }; + assert_eq!(name.to_string(), "x"); + assert_eq!(typ.to_string(), "Field"); + assert_eq!(default_value.unwrap().to_string(), "1"); + } - let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatementKind::TraitImpl(trait_impl) = top_level_statement else { - panic!("Expected to parse a trait impl"); + #[test] + fn parse_trait_with_function_no_body() { + let src = "trait Foo { fn foo(); }"; + let mut noir_trait = parse_trait_no_errors(src); + assert_eq!(noir_trait.items.len(), 1); + + let item = noir_trait.items.remove(0).item; + let TraitItem::Function { body, .. } = item else { + panic!("Expected function"); }; + assert!(body.is_none()); + } - assert!(trait_impl.items.is_empty()); + #[test] + fn parse_trait_with_function_with_body() { + let src = "trait Foo { fn foo() {} }"; + let mut noir_trait = parse_trait_no_errors(src); + assert_eq!(noir_trait.items.len(), 1); + + let item = noir_trait.items.remove(0).item; + let TraitItem::Function { body, .. } = item else { + panic!("Expected function"); + }; + assert!(body.is_some()); } } diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs new file mode 100644 index 00000000000..52815dc3783 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -0,0 +1,92 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Ident, ItemVisibility, NoirTypeAlias, UnresolvedType, UnresolvedTypeData}, + token::Token, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + /// TypeAlias = 'type' identifier Generics '=' Type ';' + pub(crate) fn parse_type_alias( + &mut self, + visibility: ItemVisibility, + start_span: Span, + ) -> NoirTypeAlias { + let Some(name) = self.eat_ident() else { + self.expected_identifier(); + return NoirTypeAlias { + visibility, + name: Ident::default(), + generics: Vec::new(), + typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + span: start_span, + }; + }; + + let generics = self.parse_generics(); + + if !self.eat_assign() { + self.expected_token(Token::Assign); + + let span = self.span_since(start_span); + self.eat_semicolons(); + + return NoirTypeAlias { + visibility, + name, + generics, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + span, + }; + } + + let typ = self.parse_type_or_error(); + let span = self.span_since(start_span); + if !self.eat_semicolons() { + self.expected_token(Token::Semicolon); + } + + NoirTypeAlias { visibility, name, generics, typ, span } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{NoirTypeAlias, UnresolvedTypeData}, + parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }, + }; + + fn parse_type_alias_no_errors(src: &str) -> NoirTypeAlias { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::TypeAlias(alias) = item.kind else { + panic!("Expected global"); + }; + alias + } + + #[test] + fn parse_type_alias_no_generics() { + let src = "type Foo = Field;"; + let alias = parse_type_alias_no_errors(src); + assert_eq!("Foo", alias.name.to_string()); + assert!(alias.generics.is_empty()); + assert_eq!(alias.typ.typ, UnresolvedTypeData::FieldElement); + } + + #[test] + fn parse_type_alias_with_generics() { + let src = "type Foo = Field;"; + let alias = parse_type_alias_no_errors(src); + assert_eq!("Foo", alias.name.to_string()); + assert_eq!(alias.generics.len(), 1); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs new file mode 100644 index 00000000000..c3f27d9d49a --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -0,0 +1,637 @@ +use crate::{ + ast::{ + Expression, ExpressionKind, GenericTypeArgs, Literal, UnresolvedType, UnresolvedTypeData, + UnresolvedTypeExpression, + }, + parser::{labels::ParsingRuleLabel, ParserError, ParserErrorReason}, + token::Token, + BinaryTypeOperator, +}; + +use acvm::acir::AcirField; +use noirc_errors::Span; + +use super::{parse_many::separated_by_comma_until_right_paren, Parser}; + +impl<'a> Parser<'a> { + /// TypeExpression= AddOrSubtractTypeExpression + pub(crate) fn parse_type_expression( + &mut self, + ) -> Result { + match self.parse_add_or_subtract_type_expression() { + Some(type_expr) => Ok(type_expr), + None => self.expected_type_expression_after_this(), + } + } + + /// AddOrSubtractTypeExpression + /// = MultiplyOrDivideOrModuloTypeExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloTypeExpression )* + fn parse_add_or_subtract_type_expression(&mut self) -> Option { + let start_span = self.current_token_span; + let lhs = self.parse_multiply_or_divide_or_modulo_type_expression()?; + Some(self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span)) + } + + fn parse_add_or_subtract_type_expression_after_lhs( + &mut self, + mut lhs: UnresolvedTypeExpression, + start_span: Span, + ) -> UnresolvedTypeExpression { + loop { + let operator = if self.eat(Token::Plus) { + BinaryTypeOperator::Addition + } else if self.eat(Token::Minus) { + BinaryTypeOperator::Subtraction + } else { + break; + }; + + match self.parse_multiply_or_divide_or_modulo_type_expression() { + Some(rhs) => { + let span = self.span_since(start_span); + lhs = UnresolvedTypeExpression::BinaryOperation( + Box::new(lhs), + operator, + Box::new(rhs), + span, + ); + } + None => { + self.push_expected_expression(); + } + } + } + + lhs + } + + /// MultiplyOrDivideOrModuloTypeExpression + /// = TermTypeExpression ( ( '*' | '/' | '%' ) TermTypeExpression )* + fn parse_multiply_or_divide_or_modulo_type_expression( + &mut self, + ) -> Option { + let start_span = self.current_token_span; + let lhs = self.parse_term_type_expression()?; + Some(self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span)) + } + + fn parse_multiply_or_divide_or_modulo_type_expression_after_lhs( + &mut self, + mut lhs: UnresolvedTypeExpression, + start_span: Span, + ) -> UnresolvedTypeExpression { + loop { + let operator = if self.eat(Token::Star) { + BinaryTypeOperator::Multiplication + } else if self.eat(Token::Slash) { + BinaryTypeOperator::Division + } else if self.eat(Token::Percent) { + BinaryTypeOperator::Modulo + } else { + break; + }; + + match self.parse_term_type_expression() { + Some(rhs) => { + let span = self.span_since(start_span); + lhs = UnresolvedTypeExpression::BinaryOperation( + Box::new(lhs), + operator, + Box::new(rhs), + span, + ); + } + None => { + self.push_expected_expression(); + break; + } + } + } + + lhs + } + + /// TermTypeExpression + /// = '- TermTypeExpression + /// | AtomTypeExpression + fn parse_term_type_expression(&mut self) -> Option { + let start_span = self.current_token_span; + if self.eat(Token::Minus) { + return match self.parse_term_type_expression() { + Some(rhs) => { + let lhs = UnresolvedTypeExpression::Constant(0, start_span); + let op = BinaryTypeOperator::Subtraction; + let span = self.span_since(start_span); + Some(UnresolvedTypeExpression::BinaryOperation( + Box::new(lhs), + op, + Box::new(rhs), + span, + )) + } + None => { + self.push_expected_expression(); + None + } + }; + } + + self.parse_atom_type_expression() + } + + /// AtomTypeExpression + /// = ConstantTypeExpression + /// | VariableTypeExpression + /// | ParenthesizedTypeExpression + fn parse_atom_type_expression(&mut self) -> Option { + if let Some(type_expr) = self.parse_constant_type_expression() { + return Some(type_expr); + } + + if let Some(type_expr) = self.parse_variable_type_expression() { + return Some(type_expr); + } + + if let Some(type_expr) = self.parse_parenthesized_type_expression() { + return Some(type_expr); + } + + None + } + + /// ConstantTypeExpression = int + fn parse_constant_type_expression(&mut self) -> Option { + let Some(int) = self.eat_int() else { + return None; + }; + + let int = if let Some(int) = int.try_to_u32() { + int + } else { + let err_expr = Expression { + kind: ExpressionKind::Literal(Literal::Integer(int, false)), + span: self.previous_token_span, + }; + self.push_error( + ParserErrorReason::InvalidTypeExpression(err_expr), + self.previous_token_span, + ); + 0 + }; + + Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) + } + + /// VariableTypeExpression = Path + fn parse_variable_type_expression(&mut self) -> Option { + let path = self.parse_path()?; + Some(UnresolvedTypeExpression::Variable(path)) + } + + /// ParenthesizedTypeExpression = '(' TypeExpression ')' + fn parse_parenthesized_type_expression(&mut self) -> Option { + // Make sure not to parse `()` as a parenthesized expression + if self.at(Token::LeftParen) && !self.next_is(Token::RightParen) { + self.bump(); + match self.parse_type_expression() { + Ok(type_expr) => { + self.eat_or_error(Token::RightParen); + Some(type_expr) + } + Err(error) => { + self.errors.push(error); + self.eat_right_paren(); + None + } + } + } else { + None + } + } + + /// TypeOrTypeExpression = Type | TypeExpression + pub(crate) fn parse_type_or_type_expression(&mut self) -> Option { + let typ = self.parse_add_or_subtract_type_or_type_expression()?; + let span = typ.span; + + // If we end up with a Variable type expression, make it a Named type (they are equivalent), + // but for testing purposes and simplicity we default to types instead of type expressions. + Some( + if let UnresolvedTypeData::Expression(UnresolvedTypeExpression::Variable(path)) = + typ.typ + { + UnresolvedType { + typ: UnresolvedTypeData::Named(path, GenericTypeArgs::default(), false), + span, + } + } else { + typ + }, + ) + } + + fn parse_add_or_subtract_type_or_type_expression(&mut self) -> Option { + let start_span = self.current_token_span; + let lhs = self.parse_multiply_or_divide_or_modulo_type_or_type_expression()?; + + // If lhs is a type then no operator can follow, so we stop right away + if !type_is_type_expr(&lhs) { + return Some(lhs); + } + + let lhs = type_to_type_expr(lhs).unwrap(); + let lhs = self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span); + Some(type_expr_to_type(lhs, self.span_since(start_span))) + } + + fn parse_multiply_or_divide_or_modulo_type_or_type_expression( + &mut self, + ) -> Option { + let start_span = self.current_token_span; + let lhs = self.parse_term_type_or_type_expression()?; + + // If lhs is a type then no operator can follow, so we stop right away + if !type_is_type_expr(&lhs) { + return Some(lhs); + } + + let lhs = type_to_type_expr(lhs).unwrap(); + let lhs = + self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span); + Some(type_expr_to_type(lhs, self.span_since(start_span))) + } + + fn parse_term_type_or_type_expression(&mut self) -> Option { + let start_span = self.current_token_span; + if self.eat(Token::Minus) { + // If we ate '-' what follows must be a type expression, never a type + return match self.parse_term_type_expression() { + Some(rhs) => { + let lhs = UnresolvedTypeExpression::Constant(0, start_span); + let op = BinaryTypeOperator::Subtraction; + let span = self.span_since(start_span); + let type_expr = UnresolvedTypeExpression::BinaryOperation( + Box::new(lhs), + op, + Box::new(rhs), + span, + ); + let typ = UnresolvedTypeData::Expression(type_expr); + Some(UnresolvedType { typ, span }) + } + None => { + self.push_expected_expression(); + None + } + }; + } + + self.parse_atom_type_or_type_expression() + } + + fn parse_atom_type_or_type_expression(&mut self) -> Option { + let start_span = self.current_token_span; + + if let Some(path) = self.parse_path() { + let generics = self.parse_generic_type_args(); + let typ = UnresolvedTypeData::Named(path, generics, false); + let span = self.span_since(start_span); + return Some(UnresolvedType { typ, span }); + } + + if let Some(type_expr) = self.parse_constant_type_expression() { + let typ = UnresolvedTypeData::Expression(type_expr); + let span = self.span_since(start_span); + return Some(UnresolvedType { typ, span }); + } + + if let Some(typ) = self.parse_parenthesized_type_or_type_expression() { + return Some(typ); + } + + self.parse_type() + } + + fn parse_parenthesized_type_or_type_expression(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.eat_left_paren() { + return None; + } + + if self.eat_right_paren() { + return Some(UnresolvedType { + typ: UnresolvedTypeData::Unit, + span: self.span_since(start_span), + }); + } + + let Some(typ) = self.parse_type_or_type_expression() else { + self.expected_label(ParsingRuleLabel::TypeOrTypeExpression); + return None; + }; + + // If what we just parsed is a type expression then this must be a parenthesized type + // expression (there's no such thing as a tuple of type expressions) + if let UnresolvedTypeData::Expression(type_expr) = typ.typ { + self.eat_or_error(Token::RightParen); + return Some(UnresolvedType { + typ: UnresolvedTypeData::Expression(type_expr), + span: typ.span, + }); + } + + if self.eat_right_paren() { + return Some(UnresolvedType { + typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), + span: self.span_since(start_span), + }); + } + + let comma_after_first_type = self.eat_comma(); + let second_type_span = self.current_token_span; + + let mut types = self.parse_many( + "tuple items", + separated_by_comma_until_right_paren(), + Self::parse_type_in_list, + ); + + if !types.is_empty() && !comma_after_first_type { + self.expected_token_separating_items(Token::Comma, "tuple items", second_type_span); + } + + types.insert(0, typ); + + Some(UnresolvedType { + typ: UnresolvedTypeData::Tuple(types), + span: self.span_since(start_span), + }) + } + + fn expected_type_expression_after_this( + &mut self, + ) -> Result { + Err(ParserError::expected_label( + ParsingRuleLabel::TypeExpression, + self.token.token().clone(), + self.current_token_span, + )) + } +} + +fn type_to_type_expr(typ: UnresolvedType) -> Option { + match typ.typ { + UnresolvedTypeData::Named(var, generics, _) => { + if generics.is_empty() { + Some(UnresolvedTypeExpression::Variable(var)) + } else { + None + } + } + UnresolvedTypeData::Expression(type_expr) => Some(type_expr), + _ => None, + } +} + +fn type_is_type_expr(typ: &UnresolvedType) -> bool { + match &typ.typ { + UnresolvedTypeData::Named(_, generics, _) => generics.is_empty(), + UnresolvedTypeData::Expression(..) => true, + _ => false, + } +} + +fn type_expr_to_type(lhs: UnresolvedTypeExpression, span: Span) -> UnresolvedType { + let lhs = UnresolvedTypeData::Expression(lhs); + UnresolvedType { typ: lhs, span } +} + +#[cfg(test)] +mod tests { + use core::panic; + + use crate::{ + ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + token::Token, + BinaryTypeOperator, + }; + + fn parse_type_expression_no_errors(src: &str) -> UnresolvedTypeExpression { + let mut parser = Parser::for_str(src); + let expr = parser.parse_type_expression().unwrap(); + expect_no_errors(&parser.errors); + expr + } + + fn parse_type_or_type_expression_no_errors(src: &str) -> UnresolvedType { + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + expect_no_errors(&parser.errors); + typ + } + + #[test] + fn parses_constant_type_expression() { + let src = "42"; + let expr = parse_type_expression_no_errors(src); + let UnresolvedTypeExpression::Constant(n, _) = expr else { + panic!("Expected constant"); + }; + assert_eq!(n, 42); + } + + #[test] + fn parses_variable_type_expression() { + let src = "foo::bar"; + let expr = parse_type_expression_no_errors(src); + let UnresolvedTypeExpression::Variable(path) = expr else { + panic!("Expected path"); + }; + assert_eq!(path.to_string(), "foo::bar"); + } + + #[test] + fn parses_binary_type_expression() { + let src = "1 + 2 * 3 + 4"; + let expr = parse_type_expression_no_errors(src); + let UnresolvedTypeExpression::BinaryOperation(lhs, operator, rhs, _) = expr else { + panic!("Expected binary operation"); + }; + assert_eq!(lhs.to_string(), "(1 + (2 * 3))"); + assert_eq!(operator, BinaryTypeOperator::Addition); + assert_eq!(rhs.to_string(), "4"); + } + + #[test] + fn parses_parenthesized_type_expression() { + let src = "(N)"; + let expr = parse_type_expression_no_errors(src); + let UnresolvedTypeExpression::Variable(path) = expr else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "N"); + } + + #[test] + fn parses_minus_type_expression() { + let src = "-N"; + let expr = parse_type_expression_no_errors(src); + assert_eq!(expr.to_string(), "(0 - N)"); + } + + #[test] + fn parse_type_or_type_expression_constant() { + let src = "42"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Expression(expr) = typ.typ else { + panic!("Expected expression"); + }; + let UnresolvedTypeExpression::Constant(n, _) = expr else { + panic!("Expected constant"); + }; + assert_eq!(n, 42); + } + + #[test] + fn parse_type_or_type_expression_variable() { + let src = "foo::Bar"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { + panic!("Expected named type"); + }; + assert_eq!(path.to_string(), "foo::Bar"); + assert!(generics.is_empty()); + } + + #[test] + fn parses_type_or_type_expression_binary() { + let src = "1 + 2 * 3 + 4"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Expression(expr) = typ.typ else { + panic!("Expected expression"); + }; + let UnresolvedTypeExpression::BinaryOperation(lhs, operator, rhs, _) = expr else { + panic!("Expected binary operation"); + }; + assert_eq!(lhs.to_string(), "(1 + (2 * 3))"); + assert_eq!(operator, BinaryTypeOperator::Addition); + assert_eq!(rhs.to_string(), "4"); + } + + #[test] + fn parses_type_or_type_expression_minus() { + let src = "-N"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Expression(expr) = typ.typ else { + panic!("Expected expression"); + }; + assert_eq!(expr.to_string(), "(0 - N)"); + } + + #[test] + fn parses_type_or_type_expression_unit() { + let src = "()"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Unit = typ.typ else { + panic!("Expected unit type"); + }; + } + + #[test] + fn parses_type_or_type_expression_parenthesized_type() { + let src = "(Field)"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { + panic!("Expected parenthesized type"); + }; + let UnresolvedTypeData::FieldElement = typ.typ else { + panic!("Expected field type"); + }; + } + + #[test] + fn parses_type_or_type_expression_parenthesized_constant() { + let src = "(1)"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Expression(expr) = typ.typ else { + panic!("Expected expression type"); + }; + assert_eq!(expr.to_string(), "1"); + } + + #[test] + fn parses_type_or_type_expression_tuple_type() { + let src = "(Field, bool)"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Tuple(types) = typ.typ else { + panic!("Expected tuple type"); + }; + let UnresolvedTypeData::FieldElement = types[0].typ else { + panic!("Expected field type"); + }; + let UnresolvedTypeData::Bool = types[1].typ else { + panic!("Expected bool type"); + }; + } + + #[test] + fn parses_type_or_type_expression_tuple_type_missing_comma() { + let src = " + (Field bool) + ^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + + let typ = parser.parse_type_or_type_expression().unwrap(); + + let reason = get_single_error_reason(&parser.errors, span); + let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { + panic!("Expected a different error"); + }; + assert_eq!(token, &Token::Comma); + assert_eq!(*items, "tuple items"); + + let UnresolvedTypeData::Tuple(types) = typ.typ else { + panic!("Expected tuple type"); + }; + let UnresolvedTypeData::FieldElement = types[0].typ else { + panic!("Expected field type"); + }; + let UnresolvedTypeData::Bool = types[1].typ else { + panic!("Expected bool type"); + }; + } + + #[test] + fn parses_type_or_type_expression_tuple_type_single_element() { + let src = "(Field,)"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + expect_no_errors(&parser.errors); + let UnresolvedTypeData::Tuple(types) = typ.typ else { + panic!("Expected tuple type"); + }; + assert_eq!(types.len(), 1); + let UnresolvedTypeData::FieldElement = types[0].typ else { + panic!("Expected field type"); + }; + } + + #[test] + fn parses_type_or_type_expression_var_minus_one() { + let src = "N - 1"; + let typ = parse_type_or_type_expression_no_errors(src); + let UnresolvedTypeData::Expression(expr) = typ.typ else { + panic!("Expected expression type"); + }; + assert_eq!(expr.to_string(), "(N - 1)"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 9dd41d1a288..6702704d32c 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,412 +1,679 @@ -use super::path::{as_trait_path, path_no_turbofish}; -use super::primitives::{ident, token_kind}; -use super::{ - expression_with_precedence, keyword, nothing, parenthesized, NoirParser, ParserError, - ParserErrorReason, Precedence, +use crate::{ + ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, + token::{Keyword, Token, TokenKind}, + QuotedType, }; -use crate::ast::{ - Expression, GenericTypeArg, GenericTypeArgs, Recoverable, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, -}; -use crate::QuotedType; -use crate::parser::labels::ParsingRuleLabel; -use crate::token::{Keyword, Token, TokenKind}; +use super::{parse_many::separated_by_comma_until_right_paren, Parser}; -use chumsky::prelude::*; -use noirc_errors::Span; +impl<'a> Parser<'a> { + pub(crate) fn parse_type_or_error(&mut self) -> UnresolvedType { + if let Some(typ) = self.parse_type() { + typ + } else { + self.expected_label(ParsingRuleLabel::Type); + self.unspecified_type_at_previous_token_end() + } + } -pub fn parse_type<'a>() -> impl NoirParser + 'a { - recursive(parse_type_inner) -} + pub(crate) fn parse_type(&mut self) -> Option { + let start_span = self.current_token_span; + let typ = self.parse_unresolved_type_data()?; + let span = self.span_since(start_span); + Some(UnresolvedType { typ, span }) + } -pub(super) fn parse_type_inner<'a>( - recursive_type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - choice(( - primitive_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.clone()), - as_trait_path_type(recursive_type_parser), - )) -} + fn parse_unresolved_type_data(&mut self) -> Option { + if let Some(typ) = self.parse_primitive_type() { + return Some(typ); + } -pub(super) fn primitive_type() -> impl NoirParser { - choice(( - field_type(), - int_type(), - bool_type(), - string_type(), - comptime_type(), - resolved_type(), - interned_unresolved_type(), - )) -} + if let Some(typ) = self.parse_parentheses_type() { + return Some(typ); + } -fn as_trait_path_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - as_trait_path(type_parser) - .map_with_span(|path, span| UnresolvedTypeData::AsTraitPath(Box::new(path)).with_span(span)) -} + if let Some(typ) = self.parse_array_or_slice_type() { + return Some(typ); + } -pub(super) 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, - }) -} + if let Some(typ) = self.parses_mutable_reference_type() { + return Some(typ); + } -pub(super) fn maybe_comp_time() -> impl NoirParser { - keyword(Keyword::Comptime).or_not().map(|opt| opt.is_some()) -} + if let Some(typ) = self.parse_function_type() { + return Some(typ); + } -pub(super) fn field_type() -> impl NoirParser { - keyword(Keyword::Field) - .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) -} + if let Some(typ) = self.parse_trait_as_type() { + return Some(typ); + } -pub(super) fn bool_type() -> impl NoirParser { - keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) -} + if let Some(typ) = self.parse_as_trait_path_type() { + return Some(typ); + } -pub(super) fn comptime_type() -> impl NoirParser { - choice(( - expr_type(), - struct_definition_type(), - trait_constraint_type(), - trait_definition_type(), - trait_impl_type(), - unresolved_type_type(), - function_definition_type(), - module_type(), - type_of_quoted_types(), - top_level_item_type(), - quoted_type(), - typed_expr_type(), - comptime_string_type(), - )) -} + if let Some(path) = self.parse_path_no_turbofish() { + let generics = self.parse_generic_type_args(); + return Some(UnresolvedTypeData::Named(path, generics, false)); + } -/// 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::Quoted(QuotedType::Expr).with_span(span)) -} + None + } -/// This is the type `StructDefinition` - the type of a quoted struct definition -pub(super) fn struct_definition_type() -> impl NoirParser { - keyword(Keyword::StructDefinition).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::StructDefinition).with_span(span) - }) -} + pub(super) fn parse_primitive_type(&mut self) -> Option { + if let Some(typ) = self.parse_field_type() { + return Some(typ); + } -/// This is the type `TraitConstraint` - the type of a quoted trait constraint -pub(super) fn trait_constraint_type() -> impl NoirParser { - keyword(Keyword::TraitConstraint).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::TraitConstraint).with_span(span) - }) -} + if let Some(typ) = self.parse_int_type() { + return Some(typ); + } -pub(super) fn trait_definition_type() -> impl NoirParser { - keyword(Keyword::TraitDefinition).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::TraitDefinition).with_span(span) - }) -} + if let Some(typ) = self.parse_bool_type() { + return Some(typ); + } -pub(super) fn trait_impl_type() -> impl NoirParser { - keyword(Keyword::TraitImpl) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TraitImpl).with_span(span)) -} + if let Some(typ) = self.parse_str_type() { + return Some(typ); + } -pub(super) fn unresolved_type_type() -> impl NoirParser { - keyword(Keyword::UnresolvedType).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::UnresolvedType).with_span(span) - }) -} + if let Some(typ) = self.parse_fmtstr_type() { + return Some(typ); + } -pub(super) fn function_definition_type() -> impl NoirParser { - keyword(Keyword::FunctionDefinition).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::FunctionDefinition).with_span(span) - }) -} + if let Some(typ) = self.parse_comptime_type() { + return Some(typ); + } -pub(super) fn module_type() -> impl NoirParser { - keyword(Keyword::Module) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Module).with_span(span)) -} + if let Some(typ) = self.parse_resolved_type() { + return Some(typ); + } -/// This is the type `TopLevelItem` - the type of a quoted statement in the top level. -/// E.g. a type definition, trait definition, trait impl, function, etc. -fn top_level_item_type() -> impl NoirParser { - keyword(Keyword::TopLevelItem).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::TopLevelItem).with_span(span) - }) -} + if let Some(typ) = self.parse_interned_type() { + return Some(typ); + } -/// This is the type `Type` - the type of a quoted noir type. -fn type_of_quoted_types() -> impl NoirParser { - keyword(Keyword::TypeType) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Type).with_span(span)) -} + None + } -/// This is the type of a quoted, unparsed token stream. -fn quoted_type() -> impl NoirParser { - keyword(Keyword::Quoted) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Quoted).with_span(span)) -} + fn parse_bool_type(&mut self) -> Option { + if self.eat_keyword(Keyword::Bool) { + return Some(UnresolvedTypeData::Bool); + } -/// This is the type of a typed/resolved expression. -fn typed_expr_type() -> impl NoirParser { - keyword(Keyword::TypedExpr) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TypedExpr).with_span(span)) -} + None + } -/// This is the `CtString` type for dynamically-sized compile-time strings -fn comptime_string_type() -> impl NoirParser { - keyword(Keyword::CtString) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::CtString).with_span(span)) -} + fn parse_field_type(&mut self) -> Option { + if self.eat_keyword(Keyword::Field) { + return Some(UnresolvedTypeData::FieldElement); + } -/// This is the type of an already resolved type. -/// The only way this can appear in the token input is if an already resolved `Type` object -/// was spliced into a macro's token stream via the `$` operator. -pub(super) fn resolved_type() -> impl NoirParser { - token_kind(TokenKind::QuotedType).map_with_span(|token, span| match token { - Token::QuotedType(id) => UnresolvedTypeData::Resolved(id).with_span(span), - _ => unreachable!("token_kind(QuotedType) guarantees we parse a quoted type"), - }) -} + None + } -pub(super) fn interned_unresolved_type() -> impl NoirParser { - token_kind(TokenKind::InternedUnresolvedTypeData).map_with_span(|token, span| match token { - Token::InternedUnresolvedTypeData(id) => UnresolvedTypeData::Interned(id).with_span(span), - _ => unreachable!( - "token_kind(InternedUnresolvedTypeData) guarantees we parse an interned unresolved type" - ), - }) -} + fn parse_int_type(&mut self) -> Option { + if let Some(int_type) = self.eat_int_type() { + return Some(match UnresolvedTypeData::from_int_token(int_type) { + Ok(typ) => typ, + Err(err) => { + self.push_error( + ParserErrorReason::InvalidBitSize(err.0), + self.previous_token_span, + ); + UnresolvedTypeData::Error + } + }); + } -pub(super) 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)) -} + None + } -pub(super) 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 parse_str_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::String) { + return None; + } -pub(super) 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) - }, - ) - }) -} + if !self.eat_less() { + self.expected_token(Token::Less); + let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); + return Some(UnresolvedTypeData::String(expr)); + } -pub(super) fn named_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - path_no_turbofish().then(generic_type_args(type_parser)).map_with_span(|(path, args), span| { - UnresolvedTypeData::Named(path, args, false).with_span(span) - }) -} + let expr = match self.parse_type_expression() { + Ok(expr) => expr, + Err(error) => { + self.errors.push(error); + UnresolvedTypeExpression::Constant(0, self.current_token_span) + } + }; -pub(super) fn named_trait<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - keyword(Keyword::Impl) - .ignore_then(path_no_turbofish()) - .then(generic_type_args(type_parser)) - .map_with_span(|(path, args), span| { - UnresolvedTypeData::TraitAsType(path, args).with_span(span) - }) -} + self.eat_or_error(Token::Greater); -pub(super) fn generic_type_args<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - required_generic_type_args(type_parser).or_not().map(Option::unwrap_or_default) -} + Some(UnresolvedTypeData::String(expr)) + } -pub(super) fn required_generic_type_args<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - let generic_type_arg = type_parser - .clone() - .then_ignore(one_of([Token::Comma, Token::Greater]).rewind()) - .or(type_expression_validated()); - - let named_arg = ident() - .then_ignore(just(Token::Assign)) - .then(generic_type_arg.clone()) - .map(|(name, typ)| GenericTypeArg::Named(name, typ)); - - // We need to parse named arguments first since otherwise when we see - // `Foo = Bar`, just `Foo` is a valid type, and we'd parse an ordered - // generic before erroring that an `=` is invalid after an ordered generic. - choice((named_arg, generic_type_arg.map(GenericTypeArg::Ordered))) - .boxed() - // 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. - .separated_by(just(Token::Comma)) - .allow_trailing() - .at_least(1) - .delimited_by(just(Token::Less), just(Token::Greater)) - .map(GenericTypeArgs::from) -} + fn parse_fmtstr_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::FormatString) { + return None; + } -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())) - .then_ignore(just(Token::RightBracket)) - .map_with_span(|(element_type, size), span| { - UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) - }) -} + if !self.eat_less() { + self.expected_token(Token::Less); + let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); + let typ = UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()); + return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); + } -pub(super) 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) - }) -} + let expr = match self.parse_type_expression() { + Ok(expr) => expr, + Err(error) => { + self.errors.push(error); + UnresolvedTypeExpression::Constant(0, self.current_token_span) + } + }; -fn type_expression() -> impl NoirParser { - type_expression_inner().try_map(UnresolvedTypeExpression::from_expr) -} + if !self.eat_commas() { + self.expected_token(Token::Comma); + } + + let typ = self.parse_type_or_error(); + + self.eat_or_error(Token::Greater); + + Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))) + } + + fn parse_comptime_type(&mut self) -> Option { + if self.eat_keyword(Keyword::Expr) { + return Some(UnresolvedTypeData::Quoted(QuotedType::Expr)); + } + if self.eat_keyword(Keyword::Quoted) { + return Some(UnresolvedTypeData::Quoted(QuotedType::Quoted)); + } + if self.eat_keyword(Keyword::TopLevelItem) { + return Some(UnresolvedTypeData::Quoted(QuotedType::TopLevelItem)); + } + if self.eat_keyword(Keyword::TypeType) { + return Some(UnresolvedTypeData::Quoted(QuotedType::Type)); + } + if self.eat_keyword(Keyword::TypedExpr) { + return Some(UnresolvedTypeData::Quoted(QuotedType::TypedExpr)); + } + if self.eat_keyword(Keyword::StructDefinition) { + return Some(UnresolvedTypeData::Quoted(QuotedType::StructDefinition)); + } + if self.eat_keyword(Keyword::TraitConstraint) { + return Some(UnresolvedTypeData::Quoted(QuotedType::TraitConstraint)); + } + if self.eat_keyword(Keyword::TraitDefinition) { + return Some(UnresolvedTypeData::Quoted(QuotedType::TraitDefinition)); + } + if self.eat_keyword(Keyword::TraitImpl) { + return Some(UnresolvedTypeData::Quoted(QuotedType::TraitImpl)); + } + if self.eat_keyword(Keyword::UnresolvedType) { + return Some(UnresolvedTypeData::Quoted(QuotedType::UnresolvedType)); + } + if self.eat_keyword(Keyword::FunctionDefinition) { + return Some(UnresolvedTypeData::Quoted(QuotedType::FunctionDefinition)); + } + if self.eat_keyword(Keyword::Module) { + return Some(UnresolvedTypeData::Quoted(QuotedType::Module)); + } + if self.eat_keyword(Keyword::CtString) { + return Some(UnresolvedTypeData::Quoted(QuotedType::CtString)); + } + None + } -/// This parser is the same as `type_expression()`, however, it continues parsing and -/// emits a parser error in the case of an invalid type expression rather than halting the parser. -pub(super) fn type_expression_validated() -> impl NoirParser { - type_expression_inner().validate(|expr, span, emit| { - let type_expr = UnresolvedTypeExpression::from_expr(expr, span); - match type_expr { - Ok(type_expression) => UnresolvedTypeData::Expression(type_expression).with_span(span), - Err(parser_error) => { - emit(parser_error); - UnresolvedType::error(span) + fn parse_function_type(&mut self) -> Option { + let unconstrained = self.eat_keyword(Keyword::Unconstrained); + + if !self.eat_keyword(Keyword::Fn) { + if unconstrained { + self.expected_token(Token::Keyword(Keyword::Fn)); + return Some(UnresolvedTypeData::Function( + Vec::new(), + Box::new(self.unspecified_type_at_previous_token_end()), + Box::new(self.unspecified_type_at_previous_token_end()), + unconstrained, + )); } + + return None; } - }) -} -fn type_expression_inner() -> impl NoirParser { - recursive(|expr| { - expression_with_precedence( - Precedence::lowest_type_precedence(), - expr, - nothing(), - nothing(), - true, - false, - ) - }) - .labelled(ParsingRuleLabel::TypeExpression) -} + let env = if self.eat_left_bracket() { + let typ = self.parse_type_or_error(); + self.eat_or_error(Token::RightBracket); + typ + } else { + UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + }; + + if !self.eat_left_paren() { + self.expected_token(Token::LeftParen); + + return Some(UnresolvedTypeData::Function( + Vec::new(), + Box::new(self.unspecified_type_at_previous_token_end()), + Box::new(self.unspecified_type_at_previous_token_end()), + unconstrained, + )); + } + + let args = self.parse_many( + "parameters", + separated_by_comma_until_right_paren(), + Self::parse_parameter, + ); + + let ret = if self.eat(Token::Arrow) { + self.parse_type_or_error() + } else { + self.expected_token(Token::Arrow); + UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + }; + + Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) + } -pub(super) 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) + fn parse_parameter(&mut self) -> Option { + let typ = self.parse_type_or_error(); + if let UnresolvedTypeData::Error = typ.typ { + None } else { - UnresolvedTypeData::Tuple(fields).with_span(span) + Some(typ) } - }) -} + } -pub(super) 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::Unconstrained) - .or_not() - .then(keyword(Keyword::Fn)) - .map(|(unconstrained_token, _fn_token)| unconstrained_token.is_some()) - .then(env) - .then(args) - .then_ignore(just(Token::Arrow)) - .then(type_parser) - .map_with_span(|(((unconstrained, env), args), ret), span| { - UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained) - .with_span(span) - }) -} + fn parse_trait_as_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::Impl) { + return None; + } + + let Some(path) = self.parse_path_no_turbofish() else { + self.expected_label(ParsingRuleLabel::Path); + return None; + }; + + let generics = self.parse_generic_type_args(); -pub(super) 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) + Some(UnresolvedTypeData::TraitAsType(path, generics)) + } + + fn parse_as_trait_path_type(&mut self) -> Option { + let as_trait_path = self.parse_as_trait_path()?; + Some(UnresolvedTypeData::AsTraitPath(Box::new(as_trait_path))) + } + + fn parse_resolved_type(&mut self) -> Option { + if let Some(token) = self.eat_kind(TokenKind::QuotedType) { + match token.into_token() { + Token::QuotedType(id) => { + return Some(UnresolvedTypeData::Resolved(id)); + } + _ => unreachable!(), + } + } + + None + } + + pub(super) fn parse_interned_type(&mut self) -> Option { + if let Some(token) = self.eat_kind(TokenKind::InternedUnresolvedTypeData) { + match token.into_token() { + Token::InternedUnresolvedTypeData(id) => { + return Some(UnresolvedTypeData::Interned(id)); + } + _ => unreachable!(), + } + } + + None + } + + fn parses_mutable_reference_type(&mut self) -> Option { + if self.eat(Token::Ampersand) { + self.eat_keyword_or_error(Keyword::Mut); + return Some(UnresolvedTypeData::MutableReference(Box::new( + self.parse_type_or_error(), + ))); + }; + + None + } + + fn parse_array_or_slice_type(&mut self) -> Option { + if !self.eat_left_bracket() { + return None; + } + + let typ = self.parse_type_or_error(); + + if self.eat_semicolon() { + match self.parse_type_expression() { + Ok(expr) => { + self.eat_or_error(Token::RightBracket); + Some(UnresolvedTypeData::Array(expr, Box::new(typ))) + } + Err(error) => { + self.errors.push(error); + self.eat_or_error(Token::RightBracket); + Some(UnresolvedTypeData::Slice(Box::new(typ))) + } + } + } else { + self.eat_or_error(Token::RightBracket); + Some(UnresolvedTypeData::Slice(Box::new(typ))) + } + } + + fn parse_parentheses_type(&mut self) -> Option { + if !self.eat_left_paren() { + return None; + } + + if self.eat_right_paren() { + return Some(UnresolvedTypeData::Unit); + } + + let (mut types, trailing_comma) = self.parse_many_return_trailing_separator_if_any( + "tuple elements", + separated_by_comma_until_right_paren(), + Self::parse_type_in_list, + ); + + Some(if types.len() == 1 && !trailing_comma { + UnresolvedTypeData::Parenthesized(Box::new(types.remove(0))) + } else { + UnresolvedTypeData::Tuple(types) }) + } + + pub(super) fn parse_type_in_list(&mut self) -> Option { + if let Some(typ) = self.parse_type() { + Some(typ) + } else { + self.expected_label(ParsingRuleLabel::Type); + None + } + } + + /// OptionalTypeAnnotation = ( ':' Type )? + pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { + if self.eat_colon() { + self.parse_type_or_error() + } else { + self.unspecified_type_at_previous_token_end() + } + } + + pub(super) fn unspecified_type_at_previous_token_end(&self) -> UnresolvedType { + UnresolvedTypeData::Unspecified.with_span(self.span_at_previous_token_end()) + } } #[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; +mod tests { + use strum::IntoEnumIterator; + + use crate::{ + ast::{IntegerBitSize, Signedness, UnresolvedType, UnresolvedTypeData}, + parser::{ + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + Parser, + }, + QuotedType, + }; + + fn parse_type_no_errors(src: &str) -> UnresolvedType { + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + expect_no_errors(&parser.errors); + typ + } + + #[test] + fn parses_unit_type() { + let src = "()"; + let typ = parse_type_no_errors(src); + assert!(matches!(typ.typ, UnresolvedTypeData::Unit)); + } + + #[test] + fn parses_bool_type() { + let src = "bool"; + let typ = parse_type_no_errors(src); + assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); + } + + #[test] + fn parses_int_type() { + let src = "u32"; + let typ = parse_type_no_errors(src); + assert!(matches!( + typ.typ, + UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) + )); + } + + #[test] + fn parses_field_type() { + let src = "Field"; + let typ = parse_type_no_errors(src); + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parses_str_type() { + let src = "str<10>"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::String(expr) = typ.typ else { panic!("Expected a string type") }; + assert_eq!(expr.to_string(), "10"); + } + + #[test] + fn parses_fmtstr_type() { + let src = "fmtstr<10, T>"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::FormatString(expr, typ) = typ.typ else { + panic!("Expected a format string type") + }; + assert_eq!(expr.to_string(), "10"); + assert_eq!(typ.to_string(), "T"); + } + + #[test] + fn parses_comptime_types() { + for quoted_type in QuotedType::iter() { + let src = quoted_type.to_string(); + let typ = parse_type_no_errors(&src); + let UnresolvedTypeData::Quoted(parsed_qouted_type) = typ.typ else { + panic!("Expected a quoted type for {}", quoted_type) + }; + assert_eq!(parsed_qouted_type, quoted_type); + } + } + + #[test] + fn parses_tuple_type() { + let src = "(Field, bool)"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; + assert_eq!(types.len(), 2); + + let typ = types.remove(0); + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + + let typ = types.remove(0); + assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); + } + + #[test] + fn parses_tuple_type_one_element() { + let src = "(Field,)"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; + assert_eq!(types.len(), 1); + + let typ = types.remove(0); + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parses_parenthesized_type() { + let src = "(Field)"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { + panic!("Expected a parenthesized type") + }; + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parses_unclosed_parentheses_type() { + let src = "(Field"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert_eq!(parser.errors.len(), 1); + let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { + panic!("Expected a parenthesized type") + }; + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parses_mutable_reference_type() { + let src = "&mut Field"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::MutableReference(typ) = typ.typ else { + panic!("Expected a mutable reference type") + }; + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn parses_named_type_no_generics() { + let src = "foo::Bar"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { + panic!("Expected a named type") + }; + assert_eq!(path.to_string(), "foo::Bar"); + assert!(generics.is_empty()); + } + + #[test] + fn parses_slice_type() { + let src = "[Field]"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Slice(typ) = typ.typ else { panic!("Expected a slice type") }; + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } + + #[test] + fn errors_if_missing_right_bracket_after_slice_type() { + let src = " + [Field + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + parser.parse_type(); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a ] but found end of input"); + } + + #[test] + fn parses_array_type() { + let src = "[Field; 10]"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Array(expr, typ) = typ.typ else { + panic!("Expected an array type") + }; + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(expr.to_string(), "10"); + } + + #[test] + fn parses_empty_function_type() { + let src = "fn() -> Field"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Function(args, ret, env, unconstrained) = typ.typ else { + panic!("Expected a function type") + }; + assert!(args.is_empty()); + assert_eq!(ret.typ.to_string(), "Field"); + assert!(matches!(env.typ, UnresolvedTypeData::Unit)); + assert!(!unconstrained); + } + + #[test] + fn parses_function_type_with_arguments() { + let src = "fn(Field, bool) -> Field"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Function(args, _ret, _env, _unconstrained) = typ.typ else { + panic!("Expected a function type") + }; + assert_eq!(args.len(), 2); + assert_eq!(args[0].typ.to_string(), "Field"); + assert_eq!(args[1].typ.to_string(), "bool"); + } + + #[test] + fn parses_function_type_with_return_type() { + let src = "fn() -> Field"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Function(_args, ret, _env, _unconstrained) = typ.typ else { + panic!("Expected a function type") + }; + assert_eq!(ret.typ.to_string(), "Field"); + } + + #[test] + fn parses_function_type_with_env() { + let src = "fn[Field]() -> Field"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Function(_args, _ret, env, _unconstrained) = typ.typ else { + panic!("Expected a function type") + }; + assert_eq!(env.typ.to_string(), "Field"); + } + + #[test] + fn parses_unconstrained_function_type() { + let src = "unconstrained fn() -> Field"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::Function(_args, _ret, _env, unconstrained) = typ.typ else { + panic!("Expected a function type") + }; + assert!(unconstrained); + } + + #[test] + fn parses_trait_as_type_no_generics() { + let src = "impl foo::Bar"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::TraitAsType(path, generics) = typ.typ else { + panic!("Expected trait as type") + }; + assert_eq!(path.to_string(), "foo::Bar"); + assert!(generics.is_empty()); + } #[test] - fn parse_type_expression() { - parse_all(type_expression(), vec!["(123)", "123", "(1 + 1)", "(1 + (1))"]); + fn parses_as_trait_path() { + let src = "::baz"; + let typ = parse_type_no_errors(src); + let UnresolvedTypeData::AsTraitPath(as_trait_path) = typ.typ else { + panic!("Expected as_trait_path") + }; + assert_eq!(as_trait_path.typ.typ.to_string(), "Field"); + assert_eq!(as_trait_path.trait_path.to_string(), "foo::Bar"); + assert!(as_trait_path.trait_generics.is_empty()); + assert_eq!(as_trait_path.impl_item.to_string(), "baz"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs new file mode 100644 index 00000000000..1c43732c94f --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -0,0 +1,253 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Ident, Path, PathKind, UseTree, UseTreeKind}, + parser::labels::ParsingRuleLabel, + token::{Keyword, Token}, +}; + +use super::{parse_many::separated_by_comma_until_right_brace, Parser}; + +impl<'a> Parser<'a> { + /// Use = 'use' PathKind PathNoTurbofish UseTree + /// + /// UseTree = PathNoTurbofish ( '::' '{' UseTreeList? '}' )? + /// + /// UseTreeList = UseTree (',' UseTree)* ','? + pub(super) fn parse_use_tree(&mut self) -> UseTree { + let start_span = self.current_token_span; + + let kind = self.parse_path_kind(); + + let use_tree = self.parse_use_tree_without_kind( + start_span, kind, false, // nested + ); + if !self.eat_semicolons() { + self.expected_token(Token::Semicolon); + } + use_tree + } + + pub(super) fn parse_use_tree_without_kind( + &mut self, + start_span: Span, + kind: PathKind, + nested: bool, + ) -> UseTree { + let prefix = self.parse_path_after_kind( + kind, false, // allow turbofish + false, // allow trailing double colon + start_span, + ); + let trailing_double_colon = if prefix.segments.is_empty() && kind != PathKind::Plain { + true + } else { + self.eat_double_colon() + }; + + if trailing_double_colon { + if self.eat_left_brace() { + let use_trees = self.parse_many( + "use trees", + separated_by_comma_until_right_brace(), + Self::parse_use_tree_in_list, + ); + + UseTree { prefix, kind: UseTreeKind::List(use_trees) } + } else { + self.expected_token(Token::LeftBrace); + self.parse_path_use_tree_end(prefix, nested) + } + } else { + self.parse_path_use_tree_end(prefix, nested) + } + } + + fn parse_use_tree_in_list(&mut self) -> Option { + let start_span = self.current_token_span; + + let use_tree = self.parse_use_tree_without_kind( + start_span, + PathKind::Plain, + true, // nested + ); + + // If we didn't advance at all, we are done + if start_span == self.current_token_span { + self.expected_label(ParsingRuleLabel::UseSegment); + None + } else { + Some(use_tree) + } + } + + pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path, nested: bool) -> UseTree { + if prefix.segments.is_empty() { + if nested { + self.expected_identifier(); + } else { + self.expected_label(ParsingRuleLabel::UseSegment); + } + UseTree { prefix, kind: UseTreeKind::Path(Ident::default(), None) } + } else { + let ident = prefix.segments.pop().unwrap().ident; + if self.eat_keyword(Keyword::As) { + if let Some(alias) = self.eat_ident() { + UseTree { prefix, kind: UseTreeKind::Path(ident, Some(alias)) } + } else { + self.expected_identifier(); + UseTree { prefix, kind: UseTreeKind::Path(ident, None) } + } + } else { + UseTree { prefix, kind: UseTreeKind::Path(ident, None) } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{ItemVisibility, PathKind, UseTree, UseTreeKind}, + parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }, + }; + + fn parse_use_tree_no_errors(src: &str) -> (UseTree, ItemVisibility) { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Import(use_tree, visibility) = item.kind else { + panic!("Expected import"); + }; + (use_tree, visibility) + } + + #[test] + fn parse_simple() { + let src = "use foo;"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("foo", use_tree.to_string()); + let UseTreeKind::Path(ident, alias) = &use_tree.kind else { + panic!("Expected path"); + }; + assert_eq!("foo", ident.to_string()); + assert!(alias.is_none()); + } + + #[test] + fn parse_simple_pub() { + let src = "pub use foo;"; + let (_use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Public); + } + + #[test] + fn parse_simple_pub_crate() { + let src = "pub(crate) use foo;"; + let (_use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::PublicCrate); + } + + #[test] + fn parse_simple_with_alias() { + let src = "use foo as bar;"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("foo as bar", use_tree.to_string()); + let UseTreeKind::Path(ident, alias) = use_tree.kind else { + panic!("Expected path"); + }; + assert_eq!("foo", ident.to_string()); + assert_eq!("bar", alias.unwrap().to_string()); + } + + #[test] + fn parse_with_crate_prefix() { + let src = "use crate::foo;"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Crate); + assert_eq!("crate::foo", use_tree.to_string()); + let UseTreeKind::Path(ident, alias) = use_tree.kind else { + panic!("Expected path"); + }; + assert_eq!("foo", ident.to_string()); + assert!(alias.is_none()); + } + + #[test] + fn parse_with_dep_prefix() { + let src = "use dep::foo;"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Dep); + assert_eq!("dep::foo", use_tree.to_string()); + let UseTreeKind::Path(ident, alias) = use_tree.kind else { + panic!("Expected path"); + }; + assert_eq!("foo", ident.to_string()); + assert!(alias.is_none()); + } + + #[test] + fn parse_with_super_prefix() { + let src = "use super::foo;"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Super); + assert_eq!("super::foo", use_tree.to_string()); + let UseTreeKind::Path(ident, alias) = use_tree.kind else { + panic!("Expected path"); + }; + assert_eq!("foo", ident.to_string()); + assert!(alias.is_none()); + } + + #[test] + fn parse_list() { + let src = "use foo::{bar, baz};"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("foo::{bar, baz}", use_tree.to_string()); + let UseTreeKind::List(use_trees) = &use_tree.kind else { + panic!("Expected list"); + }; + assert_eq!(use_trees.len(), 2); + } + + #[test] + fn parse_list_trailing_comma() { + let src = "use foo::{bar, baz, };"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("foo::{bar, baz}", use_tree.to_string()); + let UseTreeKind::List(use_trees) = &use_tree.kind else { + panic!("Expected list"); + }; + assert_eq!(use_trees.len(), 2); + } + + #[test] + fn parse_list_that_starts_with_crate() { + let src = "use crate::{foo, bar};"; + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!("crate::{foo, bar}", use_tree.to_string()); + } + + #[test] + fn errors_on_crate_in_subtree() { + let src = "use foo::{crate::bar}"; + let (_, errors) = parse_program(src); + assert!(!errors.is_empty()); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/visibility.rs b/compiler/noirc_frontend/src/parser/parser/visibility.rs deleted file mode 100644 index ea46becc932..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/visibility.rs +++ /dev/null @@ -1,36 +0,0 @@ -use chumsky::{ - prelude::{choice, empty, just}, - Parser, -}; - -use crate::{ - ast::{ItemVisibility, Visibility}, - parser::NoirParser, - token::{Keyword, Token}, -}; - -use super::{call_data, primitives::keyword}; - -/// visibility_modifier: 'pub(crate)'? 'pub'? '' -pub(crate) fn item_visibility() -> impl NoirParser { - let is_pub_crate = (keyword(Keyword::Pub) - .then_ignore(just(Token::LeftParen)) - .then_ignore(keyword(Keyword::Crate)) - .then_ignore(just(Token::RightParen))) - .map(|_| ItemVisibility::PublicCrate); - - let is_pub = keyword(Keyword::Pub).map(|_| ItemVisibility::Public); - - let is_private = empty().map(|_| ItemVisibility::Private); - - choice((is_pub_crate, is_pub, is_private)) -} - -pub fn visibility() -> impl NoirParser { - keyword(Keyword::Pub) - .map(|_| Visibility::Public) - .or(call_data()) - .or(keyword(Keyword::ReturnData).map(|_| Visibility::ReturnData)) - .or_not() - .map(|opt| opt.unwrap_or(Visibility::Private)) -} diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs new file mode 100644 index 00000000000..a753ffb6fd2 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -0,0 +1,188 @@ +use crate::{ + ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, + parser::labels::ParsingRuleLabel, + token::{Keyword, Token}, +}; + +use super::{ + parse_many::{separated_by, separated_by_comma}, + Parser, +}; + +impl<'a> Parser<'a> { + /// WhereClause = 'where' WhereClauseItems? + /// + /// WhereClauseItems = WhereClauseItem ( ',' WhereClauseItem )* ','? + /// + /// WhereClauseItem = Type ':' TraitBounds + pub(super) fn parse_where_clause(&mut self) -> Vec { + if !self.eat_keyword(Keyword::Where) { + return Vec::new(); + } + + // Constraints might end up being empty, but that's accepted as valid syntax + let constraints = + self.parse_many("where clauses", separated_by_comma(), Self::parse_single_where_clause); + + constraints + .into_iter() + .flat_map(|(typ, trait_bounds)| { + trait_bounds.into_iter().map(move |trait_bound| UnresolvedTraitConstraint { + typ: typ.clone(), + trait_bound, + }) + }) + .collect() + } + + fn parse_single_where_clause(&mut self) -> Option<(UnresolvedType, Vec)> { + let Some(typ) = self.parse_type() else { + return None; + }; + + self.eat_or_error(Token::Colon); + + let trait_bounds = self.parse_trait_bounds(); + + Some((typ, trait_bounds)) + } + + /// TraitBounds = TraitBound ( '+' TraitBound )? '+'? + pub(super) fn parse_trait_bounds(&mut self) -> Vec { + self.parse_many( + "trait bounds", + separated_by(Token::Plus).stop_if_separator_is_missing(), + Self::parse_trait_bound_in_list, + ) + } + + fn parse_trait_bound_in_list(&mut self) -> Option { + if let Some(trait_bound) = self.parse_trait_bound() { + Some(trait_bound) + } else { + self.expected_label(ParsingRuleLabel::TraitBound); + None + } + } + + pub(crate) fn parse_trait_bound_or_error(&mut self) -> TraitBound { + if let Some(trait_bound) = self.parse_trait_bound() { + return trait_bound; + } + + self.expected_label(ParsingRuleLabel::TraitBound); + TraitBound { + trait_path: Path { + kind: PathKind::Plain, + segments: Vec::new(), + span: self.span_at_previous_token_end(), + }, + trait_id: None, + trait_generics: GenericTypeArgs::default(), + } + } + + /// TraitBound = PathNoTurbofish GenericTypeArgs + pub(crate) fn parse_trait_bound(&mut self) -> Option { + let trait_path = self.parse_path_no_turbofish()?; + let trait_generics = self.parse_generic_type_args(); + Some(TraitBound { trait_path, trait_generics, trait_id: None }) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::UnresolvedTraitConstraint, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + token::Token, + }; + + fn parse_where_clause_no_errors(src: &str) -> Vec { + let mut parser = Parser::for_str(src); + let constraints = parser.parse_where_clause(); + expect_no_errors(&parser.errors); + constraints + } + + #[test] + fn parses_no_where_clause() { + let src = "{"; + let constraints = parse_where_clause_no_errors(src); + assert!(constraints.is_empty()); + } + + #[test] + fn parses_one_where_clause_with_two_constraints() { + let src = "where Foo: Bar + Baz"; + let mut constraints = parse_where_clause_no_errors(src); + assert_eq!(constraints.len(), 2); + + let constraint = constraints.remove(0); + assert_eq!(constraint.typ.to_string(), "Foo"); + assert_eq!(constraint.trait_bound.trait_path.to_string(), "Bar"); + assert_eq!(constraint.trait_bound.trait_generics.ordered_args[0].to_string(), "T"); + + let constraint = constraints.remove(0); + assert_eq!(constraint.typ.to_string(), "Foo"); + assert_eq!(constraint.trait_bound.trait_path.to_string(), "Baz"); + } + + #[test] + fn parses_two_where_clauses() { + let src = "where Foo: Bar, i32: Qux {"; + let mut constraints = parse_where_clause_no_errors(src); + assert_eq!(constraints.len(), 2); + + let constraint = constraints.remove(0); + assert_eq!(constraint.typ.to_string(), "Foo"); + assert_eq!(constraint.trait_bound.trait_path.to_string(), "Bar"); + assert_eq!(constraint.trait_bound.trait_generics.ordered_args[0].to_string(), "T"); + + let constraint = constraints.remove(0); + assert_eq!(constraint.typ.to_string(), "i32"); + assert_eq!(constraint.trait_bound.trait_path.to_string(), "Qux"); + } + + #[test] + fn parses_two_where_clauses_missing_comma() { + let src = " + where Foo: Bar i32: Qux { + ^^^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + let mut constraints = parser.parse_where_clause(); + + let reason = get_single_error_reason(&parser.errors, span); + let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { + panic!("Expected a different error"); + }; + assert_eq!(token, &Token::Comma); + assert_eq!(*items, "where clauses"); + + assert_eq!(constraints.len(), 2); + + let constraint = constraints.remove(0); + assert_eq!(constraint.typ.to_string(), "Foo"); + assert_eq!(constraint.trait_bound.trait_path.to_string(), "Bar"); + assert_eq!(constraint.trait_bound.trait_generics.ordered_args[0].to_string(), "T"); + + let constraint = constraints.remove(0); + assert_eq!(constraint.typ.to_string(), "i32"); + assert_eq!(constraint.trait_bound.trait_path.to_string(), "Qux"); + } + + #[test] + fn parses_where_clause_missing_trait_bound() { + let src = "where Foo: "; + let mut parser = Parser::for_str(src); + parser.parse_where_clause(); + assert!(!parser.errors.is_empty()); + } +} diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index a8d8c807797..65841b643c4 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -30,16 +30,13 @@ use crate::hir::Context; use crate::node_interner::{NodeInterner, StmtId}; use crate::hir::def_collector::dc_crate::DefCollector; +use crate::hir::def_map::{CrateDefMap, LocalModuleId}; use crate::hir_def::expr::HirExpression; use crate::hir_def::stmt::HirStatement; use crate::monomorphization::monomorphize; use crate::parser::{ItemKind, ParserErrorReason}; use crate::token::SecondaryAttribute; -use crate::ParsedModule; -use crate::{ - hir::def_map::{CrateDefMap, LocalModuleId}, - parse_program, -}; +use crate::{parse_program, ParsedModule}; use fm::FileManager; use noirc_arena::Arena; diff --git a/tooling/lsp/Cargo.toml b/tooling/lsp/Cargo.toml index 209f2afe4a4..c15895d801f 100644 --- a/tooling/lsp/Cargo.toml +++ b/tooling/lsp/Cargo.toml @@ -14,7 +14,6 @@ workspace = true [dependencies] acvm.workspace = true -chumsky.workspace = true codespan-lsp.workspace = true lsp-types.workspace = true nargo.workspace = true diff --git a/tooling/lsp/src/attribute_reference_finder.rs b/tooling/lsp/src/attribute_reference_finder.rs index 0387d35d41f..39e1385a6e8 100644 --- a/tooling/lsp/src/attribute_reference_finder.rs +++ b/tooling/lsp/src/attribute_reference_finder.rs @@ -6,7 +6,6 @@ /// will give not only the attribute function but also any type generated by it. use std::collections::BTreeMap; -use chumsky::Parser; use fm::FileId; use noirc_errors::Span; use noirc_frontend::{ @@ -16,9 +15,8 @@ use noirc_frontend::{ def_map::{CrateDefMap, LocalModuleId, ModuleId}, resolution::path_resolver::{PathResolver, StandardPathResolver}, }, - lexer::Lexer, node_interner::ReferenceId, - parser::{path_no_turbofish, ParsedSubModule}, + parser::{ParsedSubModule, Parser}, token::CustomAttribute, usage_tracker::UsageTracker, ParsedModule, @@ -96,10 +94,8 @@ impl<'a> Visitor for AttributeReferenceFinder<'a> { Some((left, _right)) => left.to_string(), None => attribute.contents.to_string(), }; - let (tokens, _) = Lexer::lex(&name); - - let parser = path_no_turbofish(); - let Ok(path) = parser.parse(tokens) else { + let mut parser = Parser::for_str(&name); + let Some(path) = parser.parse_path_no_turbofish() else { return; }; diff --git a/tooling/lsp/src/requests/completion/tests.rs b/tooling/lsp/src/requests/completion/tests.rs index e4beea48064..bc8bb75e10c 100644 --- a/tooling/lsp/src/requests/completion/tests.rs +++ b/tooling/lsp/src/requests/completion/tests.rs @@ -595,7 +595,7 @@ mod completion_tests { vec![simple_completion_item( "lambda_var", CompletionItemKind::VARIABLE, - Some("_".to_string()), + Some("i32".to_string()), )], ) .await; diff --git a/tooling/lsp/src/requests/document_symbol.rs b/tooling/lsp/src/requests/document_symbol.rs index 6c41f4dc2e5..0fc2dc4622e 100644 --- a/tooling/lsp/src/requests/document_symbol.rs +++ b/tooling/lsp/src/requests/document_symbol.rs @@ -66,6 +66,10 @@ impl<'a> DocumentSymbolCollector<'a> { } fn collect_in_type(&mut self, name: &Ident, typ: Option<&UnresolvedType>) { + if name.0.contents.is_empty() { + return; + } + let Some(name_location) = self.to_lsp_location(name.span()) else { return; }; @@ -99,6 +103,10 @@ impl<'a> DocumentSymbolCollector<'a> { typ: &UnresolvedType, default_value: Option<&Expression>, ) { + if name.0.contents.is_empty() { + return; + } + let Some(name_location) = self.to_lsp_location(name.span()) else { return; }; @@ -137,6 +145,10 @@ impl<'a> DocumentSymbolCollector<'a> { impl<'a> Visitor for DocumentSymbolCollector<'a> { fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { + if noir_function.def.name.0.contents.is_empty() { + return false; + } + let Some(location) = self.to_lsp_location(span) else { return false; }; @@ -162,6 +174,10 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { } fn visit_noir_struct(&mut self, noir_struct: &NoirStruct, span: Span) -> bool { + if noir_struct.name.0.contents.is_empty() { + return false; + } + let Some(location) = self.to_lsp_location(span) else { return false; }; @@ -213,6 +229,10 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { } fn visit_noir_trait(&mut self, noir_trait: &NoirTrait, span: Span) -> bool { + if noir_trait.name.0.contents.is_empty() { + return false; + } + let Some(location) = self.to_lsp_location(span) else { return false; }; @@ -255,6 +275,10 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { _where_clause: &[noirc_frontend::ast::UnresolvedTraitConstraint], body: &Option, ) -> bool { + if name.0.contents.is_empty() { + return false; + } + let Some(name_location) = self.to_lsp_location(name.span()) else { return false; }; @@ -308,6 +332,10 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { typ: &UnresolvedType, default_value: &Option, ) -> bool { + if name.0.contents.is_empty() { + return false; + } + self.collect_in_constant(name, typ, default_value.as_ref()); false } @@ -400,6 +428,9 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { }; let name = type_impl.object_type.typ.to_string(); + if name.is_empty() { + return false; + } let Some(name_location) = self.to_lsp_location(type_impl.object_type.span) else { return false; @@ -431,6 +462,10 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { } fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, span: Span) -> bool { + if parsed_sub_module.name.0.contents.is_empty() { + return false; + } + let Some(name_location) = self.to_lsp_location(parsed_sub_module.name.span()) else { return false; }; @@ -465,6 +500,11 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { } fn visit_global(&mut self, global: &LetStatement, span: Span) -> bool { + let name = global.pattern.to_string(); + if name.is_empty() { + return false; + } + let Some(name_location) = self.to_lsp_location(global.pattern.span()) else { return false; }; @@ -475,7 +515,7 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { #[allow(deprecated)] self.symbols.push(DocumentSymbol { - name: global.pattern.to_string(), + name, detail: None, kind: SymbolKind::CONSTANT, tags: None, @@ -634,7 +674,7 @@ mod document_symbol_tests { deprecated: None, range: Range { start: Position { line: 15, character: 7 }, - end: Position { line: 15, character: 25 }, + end: Position { line: 15, character: 24 }, }, selection_range: Range { start: Position { line: 15, character: 7 }, diff --git a/tooling/nargo_fmt/tests/expected/databus.nr b/tooling/nargo_fmt/tests/expected/databus.nr index 60934b60b2f..0e9761ed52d 100644 --- a/tooling/nargo_fmt/tests/expected/databus.nr +++ b/tooling/nargo_fmt/tests/expected/databus.nr @@ -1,2 +1,2 @@ -fn main(x: pub u8, y: call_data u8) -> return_data u32 {} +fn main(x: pub u8, y: call_data(0) u8) -> return_data u32 {} diff --git a/tooling/nargo_fmt/tests/expected/impl.nr b/tooling/nargo_fmt/tests/expected/impl.nr index 3c2fa42837a..84394f6fa1d 100644 --- a/tooling/nargo_fmt/tests/expected/impl.nr +++ b/tooling/nargo_fmt/tests/expected/impl.nr @@ -10,6 +10,12 @@ impl MyType { fn method(mut self) {} fn method(&mut self) {} + + fn method(self: Self) {} + + fn method(mut self: Self) {} + + fn method(&mut self: Self) {} } impl MyType { diff --git a/tooling/nargo_fmt/tests/input/databus.nr b/tooling/nargo_fmt/tests/input/databus.nr index 60934b60b2f..e47fcc50210 100644 --- a/tooling/nargo_fmt/tests/input/databus.nr +++ b/tooling/nargo_fmt/tests/input/databus.nr @@ -1,2 +1,2 @@ -fn main(x: pub u8, y: call_data u8) -> return_data u32 {} +fn main(x: pub u8, y: call_data(0) u8) -> return_data u32 { } diff --git a/tooling/nargo_fmt/tests/input/impl.nr b/tooling/nargo_fmt/tests/input/impl.nr index e4adb8ebd6a..53c9759ca1b 100644 --- a/tooling/nargo_fmt/tests/input/impl.nr +++ b/tooling/nargo_fmt/tests/input/impl.nr @@ -10,6 +10,12 @@ impl MyType { fn method(mut self) {} fn method(&mut self) {} + + fn method(self: Self) {} + + fn method(mut self: Self) {} + + fn method(&mut self: Self) {} } impl MyType {