From c9aedf8753d030da2ea010aa2d1e7f3c664a20e7 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 24 Sep 2024 10:39:20 -0300 Subject: [PATCH 001/229] Manual parser: use trees --- Cargo.lock | 34 +- Cargo.toml | 4 - compiler/noirc_driver/src/lib.rs | 4 +- compiler/noirc_errors/Cargo.toml | 1 - compiler/noirc_errors/src/position.rs | 22 +- compiler/noirc_frontend/Cargo.toml | 1 - .../noirc_frontend/src/elaborator/comptime.rs | 10 +- .../src/hir/comptime/interpreter/builtin.rs | 125 +- .../interpreter/builtin/builtin_helpers.rs | 22 +- .../noirc_frontend/src/hir/comptime/tests.rs | 2 +- .../noirc_frontend/src/hir/comptime/value.rs | 66 +- compiler/noirc_frontend/src/lexer/errors.rs | 7 - compiler/noirc_frontend/src/lexer/token.rs | 25 +- compiler/noirc_frontend/src/parser/errors.rs | 42 - compiler/noirc_frontend/src/parser/mod.rs | 187 +- compiler/noirc_frontend/src/parser/parser.rs | 1874 ++--------------- .../src/parser/parser/assertion.rs | 196 -- .../src/parser/parser/attributes.rs | 87 +- .../src/parser/parser/doc_comments.rs | 61 +- .../src/parser/parser/function.rs | 319 --- .../src/parser/parser/lambdas.rs | 43 - .../src/parser/parser/literals.rs | 158 -- .../noirc_frontend/src/parser/parser/path.rs | 171 -- .../src/parser/parser/primitives.rs | 166 -- .../src/parser/parser/structs.rs | 84 - .../src/parser/parser/test_helpers.rs | 122 -- .../src/parser/parser/traits.rs | 343 --- .../noirc_frontend/src/parser/parser/types.rs | 412 ---- .../src/parser/parser/use_tree.rs | 262 +++ .../src/parser/parser/visibility.rs | 36 - 30 files changed, 583 insertions(+), 4303 deletions(-) delete mode 100644 compiler/noirc_frontend/src/parser/parser/assertion.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/function.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/lambdas.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/literals.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/path.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/primitives.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/structs.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/test_helpers.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/traits.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/types.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/use_tree.rs delete mode 100644 compiler/noirc_frontend/src/parser/parser/visibility.rs diff --git a/Cargo.lock b/Cargo.lock index 6a469bd67f4..5030e43eedc 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", @@ -2902,7 +2874,6 @@ version = "0.34.0" dependencies = [ "acvm", "base64 0.21.7", - "chumsky", "codespan", "codespan-reporting", "flate2", @@ -2945,7 +2916,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 a6cfa7de07f..e834f05d168 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_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 74916d65264..66d883a6b49 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -182,8 +182,8 @@ pub type CompilationResult = Result<(T, Warnings), ErrorsAndWarnings>; pub fn file_manager_with_stdlib(root: &Path) -> FileManager { let mut file_manager = FileManager::new(root); - add_stdlib_source_to_file_manager(&mut file_manager); - add_debug_source_to_file_manager(&mut file_manager); + // add_stdlib_source_to_file_manager(&mut file_manager); + // add_debug_source_to_file_manager(&mut file_manager); file_manager } 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/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index ca441758322..099de8b4879 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}; @@ -25,7 +24,7 @@ use crate::{ Expression, ExpressionKind, HirExpression, NodeInterner, SecondaryAttribute, StructId, }, node_interner::{DefinitionKind, DependencyId, FuncId, TraitId}, - parser::{self, TopLevelStatement, TopLevelStatementKind}, + parser::{TopLevelStatement, TopLevelStatementKind}, Type, TypeBindings, UnificationError, }; @@ -263,9 +262,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 expression = parser::expression() + // .parse(tokens) + // .map_err(|mut errors| (errors.swap_remove(0).into(), location.file))?; + let expression: Expression = todo!("Parser"); let (mut func, mut arguments) = match expression.kind { ExpressionKind::Call(call) => (*call.func, call.arguments), diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 4678d29a452..c75a0a64444 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -7,10 +7,9 @@ use builtin_helpers::{ get_format_string, get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, get_u32, get_unresolved_type, has_named_attribute, hir_pattern_to_tokens, - mutate_func_meta_type, parse, quote_ident, replace_func_meta_parameters, + mutate_func_meta_type, 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; @@ -35,7 +34,6 @@ use crate::{ hir_def::function::FunctionBody, macros_api::{HirExpression, HirLiteral, Ident, ModuleDefId, NodeInterner, Signedness}, node_interner::{DefinitionKind, TraitImplKind}, - parser, token::{Attribute, SecondaryAttribute, Token}, Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, }; @@ -677,17 +675,19 @@ fn quoted_as_expr( return_type: Type, location: Location, ) -> IResult { - let argument = check_one_argument(arguments, location)?; + todo!("Parser"); + + // 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 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 expr = parse(interner, argument, parser, "an expression").ok(); + // let expr = parse(interner, argument, parser, "an expression").ok(); - option(return_type, expr) + // option(return_type, expr) } // fn as_module(quoted: Quoted) -> Option @@ -697,20 +697,21 @@ fn quoted_as_module( return_type: Type, location: Location, ) -> IResult { - let argument = check_one_argument(arguments, location)?; + todo!("Parser") + // let argument = check_one_argument(arguments, location)?; - let path = - parse(interpreter.elaborator.interner, argument, parser::path_no_turbofish(), "a path") - .ok(); - let option_value = path.and_then(|path| { - let module = interpreter - .elaborate_in_function(interpreter.current_function, |elaborator| { - elaborator.resolve_module_by_path(path) - }); - module.map(Value::ModuleDefinition) - }); + // let path = + // parse(interpreter.elaborator.interner, argument, parser::path_no_turbofish(), "a path") + // .ok(); + // let option_value = path.and_then(|path| { + // let module = interpreter + // .elaborate_in_function(interpreter.current_function, |elaborator| { + // elaborator.resolve_module_by_path(path) + // }); + // module.map(Value::ModuleDefinition) + // }); - option(return_type, option_value) + // option(return_type, option_value) } // fn as_trait_constraint(quoted: Quoted) -> TraitConstraint @@ -719,20 +720,21 @@ fn quoted_as_trait_constraint( arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - let argument = check_one_argument(arguments, location)?; - let trait_bound = parse( - interpreter.elaborator.interner, - argument, - parser::trait_bound(), - "a trait constraint", - )?; - let bound = interpreter - .elaborate_in_function(interpreter.current_function, |elaborator| { - elaborator.resolve_trait_bound(&trait_bound, Type::Unit) - }) - .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; + todo!("Parser") + // let argument = check_one_argument(arguments, location)?; + // let trait_bound = parse( + // interpreter.elaborator.interner, + // argument, + // parser::trait_bound(), + // "a trait constraint", + // )?; + // let bound = interpreter + // .elaborate_in_function(interpreter.current_function, |elaborator| { + // elaborator.resolve_trait_bound(&trait_bound, Type::Unit) + // }) + // .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; - Ok(Value::TraitConstraint(bound.trait_id, bound.trait_generics)) + // Ok(Value::TraitConstraint(bound.trait_id, bound.trait_generics)) } // fn as_type(quoted: Quoted) -> Type @@ -741,11 +743,12 @@ fn quoted_as_type( arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - let argument = check_one_argument(arguments, location)?; - let typ = parse(interpreter.elaborator.interner, argument, parser::parse_type(), "a type")?; - let typ = interpreter - .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); - Ok(Value::Type(typ)) + todo!("Parser") + // let argument = check_one_argument(arguments, location)?; + // let typ = parse(interpreter.elaborator.interner, argument, parser::parse_type(), "a type")?; + // let typ = interpreter + // .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); + // Ok(Value::Type(typ)) } // fn tokens(quoted: Quoted) -> [Quoted] @@ -2325,12 +2328,13 @@ fn function_def_set_parameters( (input_parameter, parameters_argument_location), )?; let parameter_type = get_type((tuple.pop().unwrap(), parameters_argument_location))?; - let parameter_pattern = parse( - interpreter.elaborator.interner, - (tuple.pop().unwrap(), parameters_argument_location), - parser::pattern(), - "a pattern", - )?; + let parameter_pattern = todo!("Parser"); + // let parameter_pattern = parse( + // interpreter.elaborator.interner, + // (tuple.pop().unwrap(), parameters_argument_location), + // parser::pattern(), + // "a pattern", + // )?; let hir_pattern = interpreter.elaborate_in_function(Some(func_id), |elaborator| { elaborator.elaborate_pattern_and_store_ids( @@ -2428,23 +2432,24 @@ 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 top_level_statements = - parse(interpreter.elaborator.interner, item, parser, "a top-level item")?; + // let parser = parser::top_level_items(); + // let top_level_statements = + // parse(interpreter.elaborator.interner, item, parser, "a top-level item")?; + let top_level_statements = todo!("Parser"); - interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { - let mut generated_items = CollectedItems::default(); + // interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { + // let mut generated_items = CollectedItems::default(); - for top_level_statement in top_level_statements { - elaborator.add_item(top_level_statement, &mut generated_items, location); - } + // for top_level_statement in top_level_statements { + // elaborator.add_item(top_level_statement, &mut generated_items, location); + // } - if !generated_items.is_empty() { - elaborator.elaborate_items(generated_items); - } - }); + // if !generated_items.is_empty() { + // elaborator.elaborate_items(generated_items); + // } + // }); - Ok(Value::Unit) + // Ok(Value::Unit) } fn module_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { 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 20303e49e15..0e4e0ec0afa 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 @@ -14,7 +14,7 @@ use crate::{ hir::{ comptime::{ errors::IResult, - value::{add_token_spans, ExprValue, TypedExpr}, + value::{ExprValue, TypedExpr}, Interpreter, InterpreterError, Value, }, def_map::ModuleId, @@ -409,10 +409,11 @@ pub(super) fn parse( parser: impl NoirParser, rule: &'static str, ) -> IResult { - let parser = parser.then_ignore(chumsky::primitive::end()); - let tokens = get_quoted((value, location))?; - let quoted = add_token_spans(tokens.clone(), location.span); - parse_tokens(tokens, quoted, interner, location, parser, rule) + todo!("Parser") + // let parser = parser.then_ignore(chumsky::primitive::end()); + // 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( @@ -423,11 +424,12 @@ pub(super) fn parse_tokens( parser: impl NoirParser, rule: &'static str, ) -> IResult { - parser.parse(quoted).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 } - }) + todo!("Parser") + // parser.parse(quoted).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 } + // }) } pub(super) fn mutate_func_meta_type(interner: &mut NodeInterner, func_id: FuncId, f: F) diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index 5b03b27e0b2..73d7d27f860 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 f01e188e498..8b101d8d033 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}; @@ -261,16 +260,17 @@ 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) { - Ok(expr) => Ok(expr), - Err(mut errors) => { - let error = errors.swap_remove(0); - let file = location.file; - let rule = "an expression"; - let tokens = tokens_to_string(tokens, interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) - } - }; + return todo!("Parser"); + // return match parser::expression().parse(tokens_to_parse) { + // Ok(expr) => Ok(expr), + // Err(mut errors) => { + // let error = errors.swap_remove(0); + // let file = location.file; + // let rule = "an expression"; + // let tokens = tokens_to_string(tokens, interner); + // Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + // } + // }; } Value::Expr(ExprValue::Expression(expr)) => expr, Value::Expr(ExprValue::Statement(statement)) => { @@ -524,17 +524,18 @@ impl Value { location: Location, interner: &NodeInterner, ) -> IResult> { - let parser = parser::top_level_items(); - match self { - Value::Quoted(tokens) => { - parse_tokens(tokens, interner, parser, location, "top-level item") - } - _ => { - let typ = self.get_type().into_owned(); - let value = self.display(interner).to_string(); - Err(InterpreterError::CannotInlineMacro { value, typ, location }) - } - } + todo!("Parser") + // let parser = parser::top_level_items(); + // match self { + // Value::Quoted(tokens) => { + // parse_tokens(tokens, interner, parser, location, "top-level item") + // } + // _ => { + // let typ = self.get_type().into_owned(); + // let value = self.display(interner).to_string(); + // Err(InterpreterError::CannotInlineMacro { value, typ, location }) + // } + // } } } @@ -550,16 +551,17 @@ fn parse_tokens( location: Location, rule: &'static str, ) -> IResult { - let parser = parser.then_ignore(chumsky::primitive::end()); - match parser.parse(add_token_spans(tokens.clone(), location.span)) { - Ok(expr) => Ok(expr), - Err(mut errors) => { - let error = errors.swap_remove(0); - let file = location.file; - let tokens = tokens_to_string(tokens, interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) - } - } + todo!("Parser!!") + // let parser = parser.then_ignore(chumsky::primitive::end()); + // match parser.parse(add_token_spans(tokens.clone(), location.span)) { + // Ok(expr) => Ok(expr), + // Err(mut errors) => { + // let error = errors.swap_remove(0); + // let file = location.file; + // let tokens = tokens_to_string(tokens, interner); + // Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + // } + // } } pub(crate) fn add_token_spans(tokens: Rc>, span: Span) -> Tokens { diff --git a/compiler/noirc_frontend/src/lexer/errors.rs b/compiler/noirc_frontend/src/lexer/errors.rs index 2440109af15..476ef05c90d 100644 --- a/compiler/noirc_frontend/src/lexer/errors.rs +++ b/compiler/noirc_frontend/src/lexer/errors.rs @@ -137,10 +137,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 f7e0a85c79c..7a3f4a029e0 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -308,7 +308,7 @@ pub enum DocStyle { Inner, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] pub struct SpannedToken(Spanned); impl PartialEq for Token { @@ -334,6 +334,12 @@ impl<'a> From<&'a SpannedToken> for &'a Token { } } +impl Default for Token { + fn default() -> Self { + Token::EOF + } +} + impl SpannedToken { pub fn new(token: Token, span: Span) -> SpannedToken { SpannedToken(Spanned::from(span, token)) @@ -1208,23 +1214,6 @@ 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/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 75fe1bf747f..096aa6d31f5 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -229,45 +229,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/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index 968af82a8b3..5119008c999 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -12,23 +12,17 @@ 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, Token}; -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, -}; +pub use parser::parse_program; + +pub trait NoirParser {} #[derive(Debug, Clone)] pub struct TopLevelStatement { @@ -71,175 +65,6 @@ impl TopLevelStatementKind { } } -// 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)) -} - #[derive(Clone, Default)] pub struct SortedModule { pub imports: Vec, diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index b007653062b..237312d2cdd 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,88 +1,16 @@ -//! 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 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 noirc_errors::Span; -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::{ + ast::{Ident, ItemVisibility}, + lexer::{Lexer, SpannedTokenResult}, + token::{Keyword, SpannedToken, Token, TokenKind}, }; -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::{Item, ItemKind, ParsedModule, ParserError}; -mod assertion; mod attributes; mod doc_comments; -mod function; -mod lambdas; -mod literals; -pub(super) mod path; -mod primitives; -mod structs; -pub(super) mod traits; -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; /// Entry function for the parser - also handles lexing internally. /// @@ -91,1721 +19,187 @@ 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(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, 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 } - }, - ) - .map(TopLevelStatementKind::Global) -} - -/// submodule: 'mod' ident '{' module '}' -fn submodule( - module_parser: impl NoirParser, -) -> impl NoirParser { - attributes() - .then_ignore(keyword(Keyword::Mod)) - .then(ident()) - .then_ignore(just(Token::LeftBrace)) - .then(module_parser) - .then_ignore(just(Token::RightBrace)) - .validate(|((attributes, name), contents), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::SubModule(ParsedSubModule { - name, - contents, - outer_attributes: attributes, - is_contract: false, - }) - }) -} - -/// contract: 'contract' ident '{' module '}' -fn contract( - module_parser: impl NoirParser, -) -> impl NoirParser { - attributes() - .then_ignore(keyword(Keyword::Contract)) - .then(ident()) - .then_ignore(just(Token::LeftBrace)) - .then(module_parser) - .then_ignore(just(Token::RightBrace)) - .validate(|((attributes, name), contents), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::SubModule(ParsedSubModule { - name, - contents, - outer_attributes: attributes, - is_contract: true, - }) - }) -} - -fn type_alias_definition() -> impl NoirParser { - use self::Keyword::Type; - - let p = ignore_then_commit(keyword(Type), ident()); - let p = then_commit(p, function::generics()); - let p = then_commit_ignore(p, just(Token::Assign)); - let p = then_commit(p, parse_type()); - - p.map_with_span(|((name, generics), typ), span| { - TopLevelStatementKind::TypeAlias(NoirTypeAlias { name, generics, typ, span }) - }) + let lexer = Lexer::new(source_program); + let mut parser = Parser::new(TokenStream::Lexer(lexer)); + let program = parser.parse_module(); + let errors = parser.errors; + (program, errors) } -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 } - }) +enum TokenStream<'a> { + Lexer(Lexer<'a>), } -/// 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) - } +impl<'a> TokenStream<'a> { + fn next(&mut self) -> Option { + match self { + TokenStream::Lexer(lexer) => lexer.next(), } - }); - - 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) - }) -} - -/// 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)) - }) -} - -fn module_declaration() -> impl NoirParser { - attributes().then_ignore(keyword(Keyword::Mod)).then(ident()).validate( - |(attributes, ident), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::Module(ModuleDeclaration { ident, outer_attributes: attributes }) - }, - ) -} - -fn use_statement() -> impl NoirParser { - item_visibility() - .then_ignore(keyword(Keyword::Use)) - .then(use_tree()) - .map(|(visibility, use_tree)| TopLevelStatementKind::Import(use_tree, visibility)) + } } -fn rename() -> impl NoirParser> { - ignore_then_commit(keyword(Keyword::As), ident()).or_not() +struct Parser<'a> { + errors: Vec, + tokens: TokenStream<'a>, + token: SpannedToken, + current_token_span: Span, + previous_token_span: Span, } -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 }) +impl<'a> Parser<'a> { + fn new(tokens: TokenStream<'a>) -> Self { + let mut parser = Self { + errors: Vec::new(), + tokens, + token: SpannedToken::new(Token::EOF, Default::default()), + current_token_span: Default::default(), + previous_token_span: Default::default(), }; - - 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)) -} - -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)) -} - -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) -} - -fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - let_statement(expr_parser) - .map(|((pattern, typ), expr)| StatementKind::new_let(pattern, typ, expr)) -} - -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(), - ); - } - - 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 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" - ), - }); - - let term = choice((parenthesized, dereferences, l_ident, interned)); - - let l_member_rhs = - just(Token::Dot).ignore_then(field_name()).map_with_span(LValueRhs::MemberAccess); - - let l_index = expr_parser - .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .map_with_span(LValueRhs::Index); - - 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 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) - } - } - }) -} - -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) - } else { - term(expr_parser, expr_no_constructors, statement, allow_constructors) - .boxed() - .labelled(ParsingRuleLabel::Term) - } - } 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) + parser.next_token(); + parser } -} -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 }); + fn parse_module(&mut self) -> ParsedModule { + let inner_doc_comments = self.parse_inner_doc_comments(); + let items = self.parse_top_level_items(); - 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()) - } else { - Err(ParserError::expected_label(ParsingRuleLabel::BinaryOperator, token, span)) - } - }) -} - -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, + ParsedModule { items, inner_doc_comments } } - // `(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)); - } - - 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)); + fn parse_top_level_items(&mut self) -> Vec { + let mut items = Vec::new(); - 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) - } - 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 }) - }) -} - -/// 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_ignore(just(Token::DoubleDot)) - .then(expr_no_constructors.clone()) - .map(|(start, end)| ForRange::Range(start, end)) - .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))) -} - -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) - } 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) + while let Some(item) = self.parse_item() { + items.push(item); } - })) -} - -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"]); + items } - #[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 parse_item(&mut self) -> Option { + let doc_comments = self.parse_outer_doc_comments(); - #[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"], - ); - } + let start_span = self.current_token_span; + let kind = self.parse_item_kind()?; + let span = self.span_since(start_span); - #[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, - ); + Some(Item { kind, span, doc_comments }) } - 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 parse_item_kind(&mut self) -> Option { + if let Some(kind) = self.parse_inner_attribute() { + return Some(kind); } - } - #[test] - fn parse_array() { - let valid = vec![ - "[0, 1, 2,3, 4]", - "[0,1,2,3,4,]", // Trailing commas are valid syntax - "[0;5]", - ]; + // TODO: visibility + let visibility = ItemVisibility::Private; - 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())); - } - } + if self.eat_keyword(Keyword::Use) { + let use_tree = self.parse_use_tree(); + return Some(ItemKind::Import(use_tree, visibility)); } - 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 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), - } + None } - #[test] - fn parse_slice() { - let valid = vec![ - "&[0, 1, 2,3, 4]", - "&[0,1,2,3,4,]", // Trailing commas are valid syntax - "&[0;5]", - ]; + fn next_token(&mut self) { + loop { + self.previous_token_span = self.current_token_span; - 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())); + let token = self.tokens.next(); + if let Some(token) = token { + match token { + Ok(token) => { + self.current_token_span = token.to_span(); + self.token = token; + break; + } + Err(lexer_error) => self.errors.push(lexer_error.into()), } + } else { + self.token = SpannedToken::new(Token::EOF, Default::default()); + self.current_token_span = Default::default(); + break; } } - - 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); - } - - #[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]}", - ], - ); } - #[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"], - ); - } - - #[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"]); - } - - #[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 }"], - ); - - 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, there are no inclusive ranges yet - "for i in 0..=100 {}", // Only '..' is supported, there are no inclusive ranges yet - ], - ); - } - - #[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)", "(,)"], - ); - } - - #[test] - fn parse_tuple() { - parse_all(tuple(expression()), vec!["()", "(x,)", "(a,b+2)", "(a,(b,c,),d,)"]); - } - - #[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"], - ); - } - - #[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"); - } - - #[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(); - } - - #[test] - fn parse_submodule_declaration() { - parse_with(submodule(module()), "mod foo {}").unwrap(); - parse_with(submodule(module()), "#[attr] mod foo {}").unwrap(); - } - - #[test] - fn parse_contract() { - parse_with(contract(module()), "contract foo {}").unwrap(); - parse_with(contract(module()), "#[attr] contract foo {}").unwrap(); + fn eat_kind(&mut self, kind: TokenKind) -> Option { + if self.token.kind() == kind { + let token = std::mem::take(&mut self.token); + self.next_token(); + Some(token) + } else { + None + } } - #[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!(), - } + fn eat_keyword(&mut self, keyword: Keyword) -> bool { + if let Token::Keyword(kw) = self.token.token() { + if *kw == keyword { + self.next_token(); + true } else { - let result = parse_with(&use_statement(), &use_statement_str); - assert!(result.is_err()); - None - }; + false + } + } else { + false } } - #[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); - } - - #[test] - fn parse_member_access() { - let cases = vec!["a.b", "a + b.c", "foo.bar as u32"]; - parse_all(expression(), cases); - } - - #[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(); - } - - // 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); - } - - #[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()); - } - - #[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())); - } - - #[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 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 + } } - #[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 eat_comma(&mut self) -> bool { + self.eat(Token::Comma).is_some() } - #[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 eat_commas(&mut self) -> bool { + if self.eat_comma() { + while self.eat_comma() { + // TODO: error + } + true + } else { + false + } } - #[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 eat_semicolon(&mut self) -> bool { + self.eat(Token::Semicolon).is_none() } - #[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 eat_semicolons(&mut self) -> bool { + if self.eat_semicolon() { + while self.eat_semicolon() { + // TODO: error + } + true + } else { + false + } } - #[test] - fn test_parses_member_access_without_member_name() { - let src = "{ foo. }"; - - let (Some(block_expression), errors) = parse_recover(block(fresh_statement()), src) else { - panic!("Expected to be able to parse a block expression"); - }; - - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected an identifier after ."); - - let statement = &block_expression.statements[0]; - let StatementKind::Expression(expr) = &statement.kind else { - panic!("Expected an expression statement"); - }; - - let ExpressionKind::Variable(var) = &expr.kind else { - panic!("Expected a variable expression"); - }; - - assert_eq!(var.to_string(), "foo"); + fn eat(&mut self, token: Token) -> Option<()> { + if self.token.token() == &token { + // TODO: error + self.next_token(); + Some(()) + } else { + None + } } - #[test] - fn parse_recover_impl_without_body() { - let src = "impl Foo"; - - 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"); - - let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatementKind::Impl(impl_) = top_level_statement else { - panic!("Expected to parse an impl"); - }; - - assert_eq!(impl_.object_type.to_string(), "Foo"); - assert!(impl_.methods.is_empty()); + fn span_since(&self, start_span: Span) -> Span { + let end_span = self.previous_token_span; + Span::from(start_span.start()..end_span.end()) } } 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 66d0ca29ca6..b365aa26879 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,78 +1,31 @@ -use chumsky::Parser; -use noirc_errors::Span; +use crate::token::{Attribute, Token, TokenKind}; -use crate::{ - macros_api::SecondaryAttribute, - parser::{NoirParser, ParserError, ParserErrorReason}, - token::{Attribute, Attributes, Token, TokenKind}, -}; +use super::ItemKind; -use super::primitives::token_kind; +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"), - }) -} - -pub(super) fn attributes() -> impl NoirParser> { - attribute().repeated() -} - -pub(super) fn validate_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Attributes { - let mut primary = None; - let mut secondary = Vec::new(); - - for attribute in attributes { - match attribute { - Attribute::Function(attr) => { - if primary.is_some() { - emit(ParserError::with_reason( - ParserErrorReason::MultipleFunctionAttributesFound, - span, - )); - } - primary = Some(attr); - } - Attribute::Secondary(attr) => secondary.push(attr), +impl<'a> Parser<'a> { + pub(super) fn parse_inner_attribute(&mut self) -> Option { + let token = self.eat_kind(TokenKind::InnerAttribute)?; + match token.into_token() { + Token::InnerAttribute(attribute) => Some(ItemKind::InnerAttribute(attribute)), + _ => unreachable!(), } } - 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![]; + pub(super) fn parse_attributes(&mut self) -> Vec { + let mut attributes: Vec = Vec::new(); - for attribute in attributes { - match attribute { - Attribute::Function(..) => { - emit(ParserError::with_reason( - ParserErrorReason::NoFunctionAttributesAllowedOnStruct, - span, - )); + while let Some(token) = self.eat_kind(TokenKind::Attribute) { + match token.into_token() { + Token::Attribute(attribute) => { + attributes.push(attribute.clone()); + self.next_token(); + } + _ => unreachable!(), } - Attribute::Secondary(attr) => struct_attributes.push(attr), } - } - struct_attributes -} - -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" - ), - }) + attributes + } } diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 151ff21017f..f9dd95d75f2 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -1,36 +1,39 @@ -use chumsky::Parser; +use crate::token::{DocStyle, Token, TokenKind}; -use crate::{ - parser::NoirParser, - token::{DocStyle, Token, TokenKind}, -}; +use super::Parser; -use super::primitives::token_kind; +impl<'a> Parser<'a> { + pub(super) fn parse_inner_doc_comments(&mut self) -> Vec { + let mut comments: Vec = Vec::new(); -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" - ), - }) -} + while let Some(token) = self.eat_kind(TokenKind::InnerDocComment) { + match token.into_token() { + Token::LineComment(comment, Some(DocStyle::Inner)) + | Token::BlockComment(comment, Some(DocStyle::Inner)) => { + comments.push(comment); + self.next_token(); + } + _ => unreachable!(), + } + } -pub(super) fn outer_doc_comments() -> impl NoirParser> { - outer_doc_comment().repeated() -} + comments + } -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" - ), - }) -} + pub(super) fn parse_outer_doc_comments(&mut self) -> Vec { + let mut comments: Vec = Vec::new(); + + while let Some(token) = self.eat_kind(TokenKind::OuterDocComment) { + match token.into_token() { + Token::LineComment(comment, Some(DocStyle::Outer)) + | Token::BlockComment(comment, Some(DocStyle::Outer)) => { + comments.push(comment); + self.next_token(); + } + _ => unreachable!(), + } + } -pub(super) fn inner_doc_comments() -> impl NoirParser> { - inner_doc_comment().repeated() + comments + } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs deleted file mode 100644 index dc8b968ea7a..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ /dev/null @@ -1,319 +0,0 @@ -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::{ - FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, - }, - macros_api::UnresolvedTypeData, - parser::{ParserError, ParserErrorReason}, -}; -use crate::{ - ast::{Signedness, UnresolvedGeneric, UnresolvedGenerics}, - parser::labels::ParsingRuleLabel, -}; - -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(), - ) - .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, - } - } 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, - } - }; - 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), - ) -} - -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, - )); - } - } - } - generic - }) -} - -pub(super) fn generic_type() -> impl NoirParser { - ident().map(UnresolvedGeneric::Variable) -} - -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"), - }) -} - -pub(super) fn generic() -> impl NoirParser { - generic_type().or(numeric_generic()).or(resolved_generic()) -} - -/// 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()) -} - -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)), - }, - ) -} - -fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { - let typ = parse_type().recover_via(parameter_recovery()); - - 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, - }); - - let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; - - let parameter = full_parameter.or(self_parameter); - - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; - - #[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 - ) - }", - ], - ); - } - - #[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]) {}", - ], - ); - - 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]) {}", - ], - ); - } - - #[test] - fn parse_recover_function_without_body() { - let src = "fn foo(x: i32)"; - - let (noir_function, errors) = parse_recover(function_definition(false), src); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].message, "expected { or -> after function parameters"); - - let noir_function = noir_function.unwrap(); - assert_eq!(noir_function.name(), "foo"); - assert_eq!(noir_function.parameters().len(), 1); - assert!(noir_function.def.body.statements.is_empty()); - } -} 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 5ef0b918375..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/lambdas.rs +++ /dev/null @@ -1,43 +0,0 @@ -use chumsky::{primitive::just, Parser}; - -use super::{parse_type, pattern}; -use crate::ast::{Expression, ExpressionKind, Lambda, Pattern, UnresolvedType}; -use crate::macros_api::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/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs deleted file mode 100644 index 1c9c24f5376..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ /dev/null @@ -1,171 +0,0 @@ -use crate::ast::{AsTraitPath, Ident, Path, PathKind, PathSegment, TypePath, UnresolvedType}; -use crate::macros_api::ExpressionKind; -use crate::parser::{NoirParser, ParserError, 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}; - -pub(super) fn path<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - path_inner(path_segment(type_parser)) -} - -pub fn path_no_turbofish() -> impl NoirParser { - path_inner(path_segment_no_turbofish()) -} - -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 - }); - 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, - }) -} - -/// 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 }) - }) -} - -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)); - - choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Plain))) -} - -pub(super) fn maybe_empty_path() -> impl NoirParser { - path_no_turbofish().or(empty_path()) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::parser::{ - parse_type, - parser::test_helpers::{parse_all_failing, parse_recover, parse_with}, - }; - - #[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); - } - } - - parse_all_failing(path(parse_type()), vec!["std::", "::std", "std::hash::", "foo::1"]); - } - - #[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); - } - - parse_all_failing( - path(parse_type()), - vec!["crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], - ); - } - - #[test] - fn parse_path_with_trailing_colons() { - let src = "foo::bar::"; - - let (path, errors) = parse_recover(path_no_turbofish(), src); - let path = path.unwrap(); - 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!(errors.len(), 1); - assert_eq!(errors[0].message, "expected an identifier after ::"); - } -} 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 7fcca89f70c..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ /dev/null @@ -1,166 +0,0 @@ -use chumsky::prelude::*; - -use crate::ast::{ExpressionKind, GenericTypeArgs, Ident, PathSegment, UnaryOp}; -use crate::macros_api::{StatementKind, 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/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs deleted file mode 100644 index 729f276e9b8..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ /dev/null @@ -1,84 +0,0 @@ -use chumsky::prelude::*; - -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}, -}; - -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, - attributes, - visibility, - generics, - fields, - span, - }) - }) -} - -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() -} - -#[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; - - #[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); - } -} 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/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs deleted file mode 100644 index b95319f6da0..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ /dev/null @@ -1,343 +0,0 @@ -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 crate::ast::{ - Documented, Expression, ItemVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, - TraitImplItemKind, TraitItem, UnresolvedTraitConstraint, UnresolvedType, -}; -use crate::macros_api::Pattern; -use crate::parser::spanned; -use crate::{ - parser::{ - ignore_then_commit, parenthesized, parser::primitives::keyword, NoirParser, ParserError, - ParserErrorReason, TopLevelStatementKind, - }, - token::{Keyword, 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() -} - -fn optional_default_value() -> impl NoirParser> { - ignore_then_commit(just(Token::Assign), expression()).or_not() -} - -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 }) -} - -/// 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, - } - }, - ) -} - -/// 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 }) -} - -/// 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, - )); - - 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, - }) - }) -} - -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)); - } - // 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() -} - -pub(super) fn where_clause() -> impl NoirParser> { - struct MultiTraitConstraint { - typ: UnresolvedType, - trait_bounds: Vec, - } - - 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, - }); - } - } - result - }) -} - -fn trait_bounds() -> impl NoirParser> { - trait_bound().separated_by(just(Token::Plus)).at_least(1).allow_trailing() -} - -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 } - }) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; - - #[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; }", - ], - ); - - parse_all_failing( - trait_definition(), - vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], - ); - } - - #[test] - fn parse_recover_function_without_left_brace_or_semicolon() { - let src = "fn foo(x: i32)"; - - 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"); - - let Some(TraitItem::Function { name, parameters, body, .. }) = trait_item else { - panic!("Expected to parser trait item as function"); - }; - - assert_eq!(name.to_string(), "foo"); - assert_eq!(parameters.len(), 1); - assert!(body.is_none()); - } - - #[test] - fn parse_recover_trait_without_body() { - let src = "trait Foo"; - - 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"); - - let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatementKind::Trait(trait_) = top_level_statement else { - panic!("Expected to parse a trait"); - }; - - assert_eq!(trait_.name.to_string(), "Foo"); - assert!(trait_.items.is_empty()); - } - - #[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"); - - let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatementKind::TraitImpl(trait_impl) = top_level_statement else { - panic!("Expected to parse a trait impl"); - }; - - assert!(trait_impl.items.is_empty()); - } -} diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs deleted file mode 100644 index 9dd41d1a288..00000000000 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ /dev/null @@ -1,412 +0,0 @@ -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::{ - Expression, GenericTypeArg, GenericTypeArgs, Recoverable, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, -}; -use crate::QuotedType; - -use crate::parser::labels::ParsingRuleLabel; -use crate::token::{Keyword, Token, TokenKind}; - -use chumsky::prelude::*; -use noirc_errors::Span; - -pub fn parse_type<'a>() -> impl NoirParser + 'a { - recursive(parse_type_inner) -} - -pub(super) fn parse_type_inner<'a>( - recursive_type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - choice(( - 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), - )) -} - -pub(super) fn primitive_type() -> impl NoirParser { - choice(( - field_type(), - int_type(), - bool_type(), - string_type(), - comptime_type(), - resolved_type(), - interned_unresolved_type(), - )) -} - -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)) -} - -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, - }) -} - -pub(super) fn maybe_comp_time() -> impl NoirParser { - keyword(Keyword::Comptime).or_not().map(|opt| opt.is_some()) -} - -pub(super) fn field_type() -> impl NoirParser { - keyword(Keyword::Field) - .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) -} - -pub(super) fn bool_type() -> impl NoirParser { - keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) -} - -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(), - )) -} - -/// 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)) -} - -/// 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) - }) -} - -/// 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) - }) -} - -pub(super) fn trait_definition_type() -> impl NoirParser { - keyword(Keyword::TraitDefinition).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::TraitDefinition).with_span(span) - }) -} - -pub(super) fn trait_impl_type() -> impl NoirParser { - keyword(Keyword::TraitImpl) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TraitImpl).with_span(span)) -} - -pub(super) fn unresolved_type_type() -> impl NoirParser { - keyword(Keyword::UnresolvedType).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::UnresolvedType).with_span(span) - }) -} - -pub(super) fn function_definition_type() -> impl NoirParser { - keyword(Keyword::FunctionDefinition).map_with_span(|_, span| { - UnresolvedTypeData::Quoted(QuotedType::FunctionDefinition).with_span(span) - }) -} - -pub(super) fn module_type() -> impl NoirParser { - keyword(Keyword::Module) - .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Module).with_span(span)) -} - -/// 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) - }) -} - -/// 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)) -} - -/// 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)) -} - -/// 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)) -} - -/// 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)) -} - -/// 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"), - }) -} - -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" - ), - }) -} - -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)) -} - -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) - }) -} - -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) - }, - ) - }) -} - -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) - }) -} - -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) - }) -} - -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) -} - -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) -} - -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) - }) -} - -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) - }) -} - -fn type_expression() -> impl NoirParser { - type_expression_inner().try_map(UnresolvedTypeExpression::from_expr) -} - -/// 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 type_expression_inner() -> impl NoirParser { - recursive(|expr| { - expression_with_precedence( - Precedence::lowest_type_precedence(), - expr, - nothing(), - nothing(), - true, - false, - ) - }) - .labelled(ParsingRuleLabel::TypeExpression) -} - -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) - } else { - UnresolvedTypeData::Tuple(fields).with_span(span) - } - }) -} - -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) - }) -} - -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) - }) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::parser::parser::test_helpers::*; - - #[test] - fn parse_type_expression() { - parse_all(type_expression(), vec!["(123)", "123", "(1 + 1)", "(1 + (1))"]); - } -} 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..cfd66e65c49 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -0,0 +1,262 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Ident, Path, PathKind, PathSegment, UseTree, UseTreeKind}, + token::{Keyword, Token}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(super) fn parse_use_tree(&mut self) -> UseTree { + let start_span = self.current_token_span; + + let kind = self.parse_path_kind(); + if kind != PathKind::Plain { + if self.eat(Token::DoubleColon).is_none() { + // TODO: error + } + } + + let use_tree = self.parse_use_tree_without_kind(start_span, kind); + if !self.eat_semicolons() { + // TODO: error + } + use_tree + } + + pub(super) fn parse_use_tree_without_kind( + &mut self, + start_span: Span, + kind: PathKind, + ) -> UseTree { + let mut segments = Vec::new(); + let mut trailing_double_colon = false; + + while let Some(ident) = self.eat_ident() { + let span = ident.span(); + segments.push(PathSegment { ident, generics: None, span }); + if self.eat(Token::DoubleColon).is_some() { + trailing_double_colon = true; + } else { + trailing_double_colon = false; + break; + } + } + + let span = if segments.is_empty() { start_span } else { self.span_since(start_span) }; + let prefix = Path { segments, kind, span }; + + if trailing_double_colon { + if self.eat(Token::LeftBrace).is_some() { + let mut use_trees = Vec::new(); + loop { + let current_span = self.current_token_span; + + let use_tree = + self.parse_use_tree_without_kind(self.current_token_span, PathKind::Plain); + + // If we didn't advance at all, we are done + if current_span == self.current_token_span { + break; + } + + use_trees.push(use_tree); + + self.eat_commas(); + + if self.eat(Token::RightBrace).is_some() { + break; + } + } + UseTree { prefix, kind: UseTreeKind::List(use_trees) } + } else { + // TODO: error + self.parse_path_use_tree_end(prefix) + } + } else { + self.parse_path_use_tree_end(prefix) + } + } + + pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path) -> UseTree { + if prefix.segments.is_empty() { + // TODO: error + UseTree { + prefix, + kind: UseTreeKind::Path(Ident::new(String::new(), Span::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 { + // TODO: error + UseTree { prefix, kind: UseTreeKind::Path(ident, None) } + } + } else { + UseTree { prefix, kind: UseTreeKind::Path(ident, None) } + } + } + } + + pub(super) fn parse_path_kind(&mut self) -> PathKind { + 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 + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{ItemVisibility, PathKind, UseTreeKind}, + parse_program, + parser::ItemKind, + }; + + #[test] + fn parse_simple() { + let src = "use foo;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Import(use_tree, visibility) = &item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, &ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("", use_tree.prefix.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_with_alias() { + let src = "use foo as bar;"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Import(use_tree, visibility) = item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("", use_tree.prefix.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 (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Import(use_tree, visibility) = item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Crate); + assert_eq!("crate::", use_tree.prefix.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 (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Import(use_tree, visibility) = item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Dep); + assert_eq!("dep::", use_tree.prefix.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 (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Import(use_tree, visibility) = item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Super); + assert_eq!("super::", use_tree.prefix.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 (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Import(use_tree, visibility) = &item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, &ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("foo", use_tree.prefix.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 (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Import(use_tree, visibility) = &item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, &ItemVisibility::Private); + assert_eq!(use_tree.prefix.kind, PathKind::Plain); + assert_eq!("foo", use_tree.prefix.to_string()); + let UseTreeKind::List(use_trees) = &use_tree.kind else { + panic!("Expected list"); + }; + assert_eq!(use_trees.len(), 2); + } +} 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)) -} From a8be03aac25b80597c04ea67a9e7e20ee66a0c0f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 24 Sep 2024 18:09:08 -0300 Subject: [PATCH 002/229] WIP: remove TODOs --- compiler/noirc_driver/src/lib.rs | 4 +- compiler/noirc_frontend/src/debug/mod.rs | 3 +- .../noirc_frontend/src/elaborator/comptime.rs | 9 ++-- .../interpreter/builtin/builtin_helpers.rs | 39 +++++++++-------- .../noirc_frontend/src/hir/def_map/mod.rs | 3 +- compiler/noirc_frontend/src/parser/mod.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 43 ++++++++++++++++--- .../src/parser/parser/use_tree.rs | 3 +- compiler/noirc_frontend/src/tests.rs | 7 +-- 9 files changed, 74 insertions(+), 39 deletions(-) diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 66d883a6b49..74916d65264 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -182,8 +182,8 @@ pub type CompilationResult = Result<(T, Warnings), ErrorsAndWarnings>; pub fn file_manager_with_stdlib(root: &Path) -> FileManager { let mut file_manager = FileManager::new(root); - // add_stdlib_source_to_file_manager(&mut file_manager); - // add_debug_source_to_file_manager(&mut file_manager); + add_stdlib_source_to_file_manager(&mut file_manager); + add_debug_source_to_file_manager(&mut file_manager); file_manager } diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index ed9265536f9..1d5b1441ccf 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 099de8b4879..400a235ed98 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -24,7 +24,7 @@ use crate::{ Expression, ExpressionKind, HirExpression, NodeInterner, SecondaryAttribute, StructId, }, node_interner::{DefinitionKind, DependencyId, FuncId, TraitId}, - parser::{TopLevelStatement, TopLevelStatementKind}, + parser::{parse_result, Parser, TopLevelStatement, TopLevelStatementKind}, Type, TypeBindings, UnificationError, }; @@ -262,10 +262,9 @@ 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 expression: Expression = todo!("Parser"); + let parser = Parser::for_tokens(tokens); + let expression = parse_result(parser, Parser::parse_expression) + .map_err(|mut errors| (errors.swap_remove(0).into(), location.file))?; let (mut func, mut arguments) = match expression.kind { ExpressionKind::Call(call) => (*call.func, call.arguments), 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 0e4e0ec0afa..911ba3ce6c8 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::{parse_result, Parser}; use crate::{ ast::{ BlockExpression, ExpressionKind, Ident, IntegerBitSize, LValue, Pattern, Signedness, @@ -403,33 +405,36 @@ pub(super) fn lex(input: &str) -> Vec { tokens } -pub(super) fn parse( +pub(super) fn parse( interner: &NodeInterner, (value, location): (Value, Location), - parser: impl NoirParser, + parser: F, rule: &'static str, -) -> IResult { - todo!("Parser") - // let parser = parser.then_ignore(chumsky::primitive::end()); - // let tokens = get_quoted((value, location))?; - // let quoted = add_token_spans(tokens.clone(), location.span); - // parse_tokens(tokens, quoted, interner, location, parser, rule) +) -> IResult +where + F: FnOnce(&mut Parser) -> 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( tokens: Rc>, quoted: Tokens, interner: &NodeInterner, location: Location, - parser: impl NoirParser, + parser: F, rule: &'static str, -) -> IResult { - todo!("Parser") - // parser.parse(quoted).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 } - // }) +) -> IResult +where + F: FnOnce(&mut Parser) -> T, +{ + parse_result(Parser::for_tokens(quoted), parser).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 } + }) } pub(super) fn mutate_func_meta_type(interner: &mut NodeInterner, func_id: FuncId, f: F) diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index d810e95218c..a34148680f1 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/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index 5119008c999..2bf5484aa03 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -20,7 +20,7 @@ use crate::token::{SecondaryAttribute, Token}; pub use errors::ParserError; pub use errors::ParserErrorReason; use noirc_errors::Span; -pub use parser::parse_program; +pub use parser::{parse_program, parse_result, Parser}; pub trait NoirParser {} diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 237312d2cdd..71ad97df470 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,9 +1,9 @@ use noirc_errors::Span; use crate::{ - ast::{Ident, ItemVisibility}, + ast::{Expression, Ident, ItemVisibility}, lexer::{Lexer, SpannedTokenResult}, - token::{Keyword, SpannedToken, Token, TokenKind}, + token::{Keyword, SpannedToken, Token, TokenKind, Tokens}, }; use super::{Item, ItemKind, ParsedModule, ParserError}; @@ -20,25 +20,45 @@ mod use_tree; /// failed to parse. Otherwise the Ast is guaranteed to have 0 Error nodes. pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { let lexer = Lexer::new(source_program); - let mut parser = Parser::new(TokenStream::Lexer(lexer)); + let mut parser = Parser::for_lexer(lexer); let program = parser.parse_module(); let errors = parser.errors; (program, errors) } +pub fn parse_result<'a, T, F>(mut parser: Parser<'a>, f: F) -> Result> +where + F: FnOnce(&mut Parser<'a>) -> T, +{ + let item = f(&mut parser); + if parser.errors.is_empty() { + Ok(item) + } else { + Err(parser.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) => { + if let Some(token) = tokens.0.pop() { + Some(Ok(token)) + } else { + None + } + } } } } -struct Parser<'a> { +pub struct Parser<'a> { errors: Vec, tokens: TokenStream<'a>, token: SpannedToken, @@ -47,6 +67,15 @@ struct Parser<'a> { } impl<'a> Parser<'a> { + pub fn for_lexer(lexer: Lexer<'a>) -> Self { + Self::new(TokenStream::Lexer(lexer)) + } + + pub fn for_tokens(mut tokens: Tokens) -> Self { + tokens.0.reverse(); + Self::new(TokenStream::Tokens(tokens)) + } + fn new(tokens: TokenStream<'a>) -> Self { let mut parser = Self { errors: Vec::new(), @@ -59,13 +88,17 @@ impl<'a> Parser<'a> { parser } - fn parse_module(&mut self) -> ParsedModule { + pub(crate) fn parse_module(&mut self) -> ParsedModule { let inner_doc_comments = self.parse_inner_doc_comments(); let items = self.parse_top_level_items(); ParsedModule { items, inner_doc_comments } } + pub(crate) fn parse_expression(&mut self) -> Expression { + todo!("Parser") + } + fn parse_top_level_items(&mut self) -> Vec { let mut items = Vec::new(); diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index cfd66e65c49..78d9fee28db 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -118,8 +118,7 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ItemVisibility, PathKind, UseTreeKind}, - parse_program, - parser::ItemKind, + parser::{parser::parse_program, ItemKind}, }; #[test] diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index ceb781266df..af33f0d6f50 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -29,16 +29,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; From f447f467815049e99f93f7ff35bca99f9bbea50a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 09:34:57 -0300 Subject: [PATCH 003/229] Move some TODOs to the parser itself, and clean up code --- compiler/noirc_frontend/src/ast/statement.rs | 8 -- .../src/hir/comptime/interpreter/builtin.rs | 129 +++++++++--------- .../interpreter/builtin/builtin_helpers.rs | 9 +- .../noirc_frontend/src/hir/comptime/value.rs | 77 +++++------ compiler/noirc_frontend/src/lexer/token.rs | 4 +- compiler/noirc_frontend/src/parser/mod.rs | 81 +---------- compiler/noirc_frontend/src/parser/parser.rs | 43 +++++- 7 files changed, 147 insertions(+), 204 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 299c42b85c6..95b32b2966c 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -645,14 +645,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 { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index c75a0a64444..0e109bed938 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -7,7 +7,7 @@ use builtin_helpers::{ get_format_string, get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, get_u32, get_unresolved_type, has_named_attribute, hir_pattern_to_tokens, - mutate_func_meta_type, quote_ident, replace_func_meta_parameters, + mutate_func_meta_type, parse, quote_ident, replace_func_meta_parameters, replace_func_meta_return_type, }; use im::Vector; @@ -34,6 +34,7 @@ use crate::{ hir_def::function::FunctionBody, macros_api::{HirExpression, HirLiteral, Ident, ModuleDefId, NodeInterner, Signedness}, node_interner::{DefinitionKind, TraitImplKind}, + parser::Parser, token::{Attribute, SecondaryAttribute, Token}, Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, }; @@ -675,19 +676,24 @@ fn quoted_as_expr( return_type: Type, location: Location, ) -> IResult { - todo!("Parser"); + let argument = check_one_argument(arguments, location)?; - // let argument = check_one_argument(arguments, location)?; + let result = parse(interner, argument.clone(), Parser::parse_expression, "an expression"); + if let Ok(expr) = result { + return option(return_type, Some(Value::expression(expr.kind))); + } - // 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_statement, "an expression"); + if let Ok(stmt) = result { + return option(return_type, Some(Value::statement(stmt.kind))); + } - // let expr = parse(interner, argument, parser, "an expression").ok(); + let result = parse(interner, argument, Parser::parse_lvalue, "an expression"); + if let Ok(lvalue) = result { + return option(return_type, Some(Value::lvalue(lvalue))); + } - // option(return_type, expr) + option(return_type, None) } // fn as_module(quoted: Quoted) -> Option @@ -697,21 +703,20 @@ fn quoted_as_module( return_type: Type, location: Location, ) -> IResult { - todo!("Parser") - // let argument = check_one_argument(arguments, location)?; + let argument = check_one_argument(arguments, location)?; - // let path = - // parse(interpreter.elaborator.interner, argument, parser::path_no_turbofish(), "a path") - // .ok(); - // let option_value = path.and_then(|path| { - // let module = interpreter - // .elaborate_in_function(interpreter.current_function, |elaborator| { - // elaborator.resolve_module_by_path(path) - // }); - // module.map(Value::ModuleDefinition) - // }); + let path = + parse(interpreter.elaborator.interner, argument, Parser::parse_path_no_turbofish, "a path") + .ok(); + let option_value = path.and_then(|path| { + let module = interpreter + .elaborate_in_function(interpreter.current_function, |elaborator| { + elaborator.resolve_module_by_path(path) + }); + module.map(Value::ModuleDefinition) + }); - // option(return_type, option_value) + option(return_type, option_value) } // fn as_trait_constraint(quoted: Quoted) -> TraitConstraint @@ -720,21 +725,20 @@ fn quoted_as_trait_constraint( arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - todo!("Parser") - // let argument = check_one_argument(arguments, location)?; - // let trait_bound = parse( - // interpreter.elaborator.interner, - // argument, - // parser::trait_bound(), - // "a trait constraint", - // )?; - // let bound = interpreter - // .elaborate_in_function(interpreter.current_function, |elaborator| { - // elaborator.resolve_trait_bound(&trait_bound, Type::Unit) - // }) - // .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; + let argument = check_one_argument(arguments, location)?; + let trait_bound = parse( + interpreter.elaborator.interner, + argument, + Parser::parse_trait_bound, + "a trait constraint", + )?; + let bound = interpreter + .elaborate_in_function(interpreter.current_function, |elaborator| { + elaborator.resolve_trait_bound(&trait_bound, Type::Unit) + }) + .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; - // Ok(Value::TraitConstraint(bound.trait_id, bound.trait_generics)) + Ok(Value::TraitConstraint(bound.trait_id, bound.trait_generics)) } // fn as_type(quoted: Quoted) -> Type @@ -743,12 +747,11 @@ fn quoted_as_type( arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - todo!("Parser") - // let argument = check_one_argument(arguments, location)?; - // let typ = parse(interpreter.elaborator.interner, argument, parser::parse_type(), "a type")?; - // let typ = interpreter - // .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); - // Ok(Value::Type(typ)) + let argument = check_one_argument(arguments, location)?; + let typ = parse(interpreter.elaborator.interner, argument, Parser::parse_type, "a type")?; + let typ = interpreter + .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); + Ok(Value::Type(typ)) } // fn tokens(quoted: Quoted) -> [Quoted] @@ -2328,13 +2331,12 @@ fn function_def_set_parameters( (input_parameter, parameters_argument_location), )?; let parameter_type = get_type((tuple.pop().unwrap(), parameters_argument_location))?; - let parameter_pattern = todo!("Parser"); - // let parameter_pattern = parse( - // interpreter.elaborator.interner, - // (tuple.pop().unwrap(), parameters_argument_location), - // parser::pattern(), - // "a pattern", - // )?; + let parameter_pattern = parse( + interpreter.elaborator.interner, + (tuple.pop().unwrap(), parameters_argument_location), + Parser::parse_pattern, + "a pattern", + )?; let hir_pattern = interpreter.elaborate_in_function(Some(func_id), |elaborator| { elaborator.elaborate_pattern_and_store_ids( @@ -2432,24 +2434,23 @@ 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 top_level_statements = - // parse(interpreter.elaborator.interner, item, parser, "a top-level item")?; - let top_level_statements = todo!("Parser"); + let parser = Parser::parse_top_level_statements; + let top_level_statements = + parse(interpreter.elaborator.interner, item, parser, "a top-level item")?; - // interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { - // let mut generated_items = CollectedItems::default(); + interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { + let mut generated_items = CollectedItems::default(); - // for top_level_statement in top_level_statements { - // elaborator.add_item(top_level_statement, &mut generated_items, location); - // } + for top_level_statement in top_level_statements { + elaborator.add_item(top_level_statement, &mut generated_items, location); + } - // if !generated_items.is_empty() { - // elaborator.elaborate_items(generated_items); - // } - // }); + if !generated_items.is_empty() { + elaborator.elaborate_items(generated_items); + } + }); - // Ok(Value::Unit) + Ok(Value::Unit) } fn module_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { 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 911ba3ce6c8..b48e9d72498 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 @@ -28,7 +28,6 @@ use crate::{ }, macros_api::{NodeInterner, StructId}, node_interner::{FuncId, TraitId, TraitImplId}, - parser::NoirParser, token::{SecondaryAttribute, Token, Tokens}, QuotedType, Type, }; @@ -405,21 +404,21 @@ 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: F, rule: &'static str, ) -> IResult where - F: FnOnce(&mut Parser) -> T, + 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, @@ -428,7 +427,7 @@ pub(super) fn parse_tokens( rule: &'static str, ) -> IResult where - F: FnOnce(&mut Parser) -> T, + F: FnOnce(&mut Parser<'a>) -> T, { parse_result(Parser::for_tokens(quoted), parser).map_err(|mut errors| { let error = errors.swap_remove(0); diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 8b101d8d033..e84e6360d2d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -18,7 +18,7 @@ use crate::{ StructId, }, node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, - parser::{self, NoirParser, TopLevelStatement}, + parser::{parse_result, Parser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, Kind, QuotedType, Shared, Type, TypeBindings, }; @@ -260,17 +260,17 @@ 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 todo!("Parser"); - // return match parser::expression().parse(tokens_to_parse) { - // Ok(expr) => Ok(expr), - // Err(mut errors) => { - // let error = errors.swap_remove(0); - // let file = location.file; - // let rule = "an expression"; - // let tokens = tokens_to_string(tokens, interner); - // Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) - // } - // }; + let parser = Parser::for_tokens(tokens_to_parse); + return match parse_result(parser, Parser::parse_expression) { + Ok(expr) => Ok(expr), + Err(mut errors) => { + let error = errors.swap_remove(0); + let file = location.file; + let rule = "an expression"; + let tokens = tokens_to_string(tokens, interner); + Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + } + }; } Value::Expr(ExprValue::Expression(expr)) => expr, Value::Expr(ExprValue::Statement(statement)) => { @@ -524,18 +524,17 @@ impl Value { location: Location, interner: &NodeInterner, ) -> IResult> { - todo!("Parser") - // let parser = parser::top_level_items(); - // match self { - // Value::Quoted(tokens) => { - // parse_tokens(tokens, interner, parser, location, "top-level item") - // } - // _ => { - // let typ = self.get_type().into_owned(); - // let value = self.display(interner).to_string(); - // Err(InterpreterError::CannotInlineMacro { value, typ, location }) - // } - // } + let parser = Parser::parse_top_level_statements; + match self { + Value::Quoted(tokens) => { + parse_tokens(tokens, interner, parser, location, "top-level item") + } + _ => { + let typ = self.get_type().into_owned(); + let value = self.display(interner).to_string(); + Err(InterpreterError::CannotInlineMacro { value, typ, location }) + } + } } } @@ -544,24 +543,26 @@ 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, + f: F, location: Location, rule: &'static str, -) -> IResult { - todo!("Parser!!") - // let parser = parser.then_ignore(chumsky::primitive::end()); - // match parser.parse(add_token_spans(tokens.clone(), location.span)) { - // Ok(expr) => Ok(expr), - // Err(mut errors) => { - // let error = errors.swap_remove(0); - // let file = location.file; - // let tokens = tokens_to_string(tokens, interner); - // Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) - // } - // } +) -> IResult +where + F: FnOnce(&mut Parser<'a>) -> T, +{ + let parser = Parser::for_tokens(add_token_spans(tokens.clone(), location.span)); + match parse_result(parser, f) { + Ok(expr) => Ok(expr), + Err(mut errors) => { + let error = errors.swap_remove(0); + let file = location.file; + let tokens = tokens_to_string(tokens, interner); + Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + } + } } pub(crate) fn add_token_spans(tokens: Rc>, span: Span) -> Tokens { diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 7a3f4a029e0..5f15f445401 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, @@ -1212,8 +1212,6 @@ impl Keyword { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Tokens(pub Vec); -type TokenMapIter = Map, fn(SpannedToken) -> (Token, Span)>; - #[cfg(test)] mod keywords { use strum::IntoEnumIterator; diff --git a/compiler/noirc_frontend/src/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index 2bf5484aa03..bc922806dc0 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -15,15 +15,13 @@ use crate::ast::{ Documented, Ident, ImportStatement, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TypeImpl, UseTree, }; -use crate::token::{SecondaryAttribute, Token}; +use crate::token::SecondaryAttribute; pub use errors::ParserError; pub use errors::ParserErrorReason; use noirc_errors::Span; pub use parser::{parse_program, parse_result, Parser}; -pub trait NoirParser {} - #[derive(Debug, Clone)] pub struct TopLevelStatement { pub kind: TopLevelStatementKind, @@ -268,83 +266,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 { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 71ad97df470..380760cbfbd 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,12 +1,15 @@ use noirc_errors::Span; use crate::{ - ast::{Expression, Ident, ItemVisibility}, + ast::{ + Expression, Ident, ItemVisibility, LValue, Path, Pattern, Statement, TraitBound, + UnresolvedType, + }, lexer::{Lexer, SpannedTokenResult}, token::{Keyword, SpannedToken, Token, TokenKind, Tokens}, }; -use super::{Item, ItemKind, ParsedModule, ParserError}; +use super::{Item, ItemKind, ParsedModule, ParserError, TopLevelStatement}; mod attributes; mod doc_comments; @@ -95,10 +98,6 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_expression(&mut self) -> Expression { - todo!("Parser") - } - fn parse_top_level_items(&mut self) -> Vec { let mut items = Vec::new(); @@ -135,6 +134,38 @@ impl<'a> Parser<'a> { None } + pub(crate) fn parse_expression(&mut self) -> Expression { + todo!("Parser") + } + + pub(crate) fn parse_top_level_statements(&mut self) -> Vec { + todo!("Parser") + } + + pub(crate) fn parse_path_no_turbofish(&mut self) -> Path { + todo!("Parser") + } + + pub(crate) fn parse_pattern(&mut self) -> Pattern { + todo!("Parser") + } + + pub(crate) fn parse_type(&mut self) -> UnresolvedType { + todo!("Parser") + } + + pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { + todo!("Parser") + } + + pub(crate) fn parse_statement(&mut self) -> Statement { + todo!("Parser") + } + + pub(crate) fn parse_lvalue(&mut self) -> LValue { + todo!("Parser") + } + fn next_token(&mut self) { loop { self.previous_token_span = self.current_token_span; From a028c16ad2920320d25cb416b5388b254895e9b3 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 09:43:05 -0300 Subject: [PATCH 004/229] No need to have TopLevelStatement --- .../noirc_frontend/src/elaborator/comptime.rs | 31 +++---- .../src/hir/comptime/interpreter/builtin.rs | 2 +- .../noirc_frontend/src/hir/comptime/value.rs | 6 +- compiler/noirc_frontend/src/parser/mod.rs | 90 +++++-------------- compiler/noirc_frontend/src/parser/parser.rs | 8 +- 5 files changed, 44 insertions(+), 93 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 400a235ed98..5bf4a9b0c36 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -24,7 +24,7 @@ use crate::{ Expression, ExpressionKind, HirExpression, NodeInterner, SecondaryAttribute, StructId, }, node_interner::{DefinitionKind, DependencyId, FuncId, TraitId}, - parser::{parse_result, Parser, TopLevelStatement, TopLevelStatementKind}, + parser::{parse_result, Item, ItemKind, Parser}, Type, TypeBindings, UnificationError, }; @@ -371,7 +371,7 @@ impl<'context> Elaborator<'context> { fn add_items( &mut self, - items: Vec, + items: Vec, generated_items: &mut CollectedItems, location: Location, ) { @@ -382,12 +382,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( @@ -408,7 +408,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, @@ -438,7 +438,7 @@ impl<'context> Elaborator<'context> { resolved_trait_generics: Vec::new(), }); } - TopLevelStatementKind::Global(global) => { + ItemKind::Global(global) => { 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 0e109bed938..03330a2b4a0 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -2434,7 +2434,7 @@ fn module_add_item( let module_id = get_module(self_argument)?; let module_data = interpreter.elaborator.get_module(module_id); - let parser = Parser::parse_top_level_statements; + 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/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index e84e6360d2d..d03906b704a 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -18,7 +18,7 @@ use crate::{ StructId, }, node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, - parser::{parse_result, Parser, TopLevelStatement}, + parser::{parse_result, Item, Parser}, token::{SpannedToken, Token, Tokens}, Kind, QuotedType, Shared, Type, TypeBindings, }; @@ -523,8 +523,8 @@ impl Value { self, location: Location, interner: &NodeInterner, - ) -> IResult> { - let parser = Parser::parse_top_level_statements; + ) -> IResult> { + let parser = Parser::parse_top_level_items; match self { Value::Quoted(tokens) => { parse_tokens(tokens, interner, parser, location, "top-level item") diff --git a/compiler/noirc_frontend/src/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index bc922806dc0..b9432b074d3 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -22,47 +22,6 @@ pub use errors::ParserErrorReason; use noirc_errors::Span; pub use parser::{parse_program, parse_result, Parser}; -#[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), - 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) => Some(ItemKind::Global(c)), - TopLevelStatementKind::InnerAttribute(a) => Some(ItemKind::InnerAttribute(a)), - TopLevelStatementKind::Error => None, - } - } -} - #[derive(Clone, Default)] pub struct SortedModule { pub imports: Vec, @@ -183,6 +142,30 @@ 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) => 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)] @@ -266,31 +249,6 @@ impl SortedModule { } } -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, "use {tree}") - } else { - write!(f, "{visibility} 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) => 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 380760cbfbd..52f65b4cef4 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -9,7 +9,7 @@ use crate::{ token::{Keyword, SpannedToken, Token, TokenKind, Tokens}, }; -use super::{Item, ItemKind, ParsedModule, ParserError, TopLevelStatement}; +use super::{Item, ItemKind, ParsedModule, ParserError}; mod attributes; mod doc_comments; @@ -98,7 +98,7 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - fn parse_top_level_items(&mut self) -> Vec { + pub(crate) fn parse_top_level_items(&mut self) -> Vec { let mut items = Vec::new(); while let Some(item) = self.parse_item() { @@ -138,10 +138,6 @@ impl<'a> Parser<'a> { todo!("Parser") } - pub(crate) fn parse_top_level_statements(&mut self) -> Vec { - todo!("Parser") - } - pub(crate) fn parse_path_no_turbofish(&mut self) -> Path { todo!("Parser") } From f1713697528e49424938b1959b6e508f043fd084 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 09:43:35 -0300 Subject: [PATCH 005/229] parse_top_level_items -> parse_items --- .../noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 2 +- compiler/noirc_frontend/src/hir/comptime/value.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 03330a2b4a0..0b0658920ab 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -2434,7 +2434,7 @@ fn module_add_item( let module_id = get_module(self_argument)?; let module_data = interpreter.elaborator.get_module(module_id); - let parser = Parser::parse_top_level_items; + let parser = Parser::parse_items; let top_level_statements = parse(interpreter.elaborator.interner, item, parser, "a top-level item")?; diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index d03906b704a..d77e8b48b16 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -524,7 +524,7 @@ impl Value { location: Location, interner: &NodeInterner, ) -> IResult> { - let parser = Parser::parse_top_level_items; + let parser = Parser::parse_items; match self { Value::Quoted(tokens) => { parse_tokens(tokens, interner, parser, location, "top-level item") diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 52f65b4cef4..c3d8c67fe31 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -93,12 +93,12 @@ impl<'a> Parser<'a> { pub(crate) fn parse_module(&mut self) -> ParsedModule { let inner_doc_comments = self.parse_inner_doc_comments(); - let items = self.parse_top_level_items(); + let items = self.parse_items(); ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_top_level_items(&mut self) -> Vec { + pub(crate) fn parse_items(&mut self) -> Vec { let mut items = Vec::new(); while let Some(item) = self.parse_item() { From e9dc76d10bad822edbbcbc628d19a3d361f26516 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 10:29:46 -0300 Subject: [PATCH 006/229] Some fixes --- compiler/noirc_frontend/src/ast/statement.rs | 10 +++--- compiler/noirc_frontend/src/parser/parser.rs | 2 +- .../src/parser/parser/use_tree.rs | 32 +++++++++++++++---- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 95b32b2966c..04af0044fcc 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -328,12 +328,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 { @@ -343,7 +343,7 @@ impl Display for UseTree { Ok(()) } UseTreeKind::List(trees) => { - write!(f, "::{{")?; + write!(f, "{{")?; let tree = vecmap(trees, ToString::to_string).join(", "); write!(f, "{tree}}}") } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index c3d8c67fe31..35e41ada48a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -234,7 +234,7 @@ impl<'a> Parser<'a> { } fn eat_semicolon(&mut self) -> bool { - self.eat(Token::Semicolon).is_none() + self.eat(Token::Semicolon).is_some() } fn eat_semicolons(&mut self) -> bool { diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 78d9fee28db..ebb07ea38e7 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -47,6 +47,10 @@ impl<'a> Parser<'a> { let span = if segments.is_empty() { start_span } else { self.span_since(start_span) }; let prefix = Path { segments, kind, span }; + if prefix.segments.is_empty() && kind != PathKind::Plain { + trailing_double_colon = true; + } + if trailing_double_colon { if self.eat(Token::LeftBrace).is_some() { let mut use_trees = Vec::new(); @@ -133,7 +137,7 @@ mod tests { }; assert_eq!(visibility, &ItemVisibility::Private); assert_eq!(use_tree.prefix.kind, PathKind::Plain); - assert_eq!("", use_tree.prefix.to_string()); + assert_eq!("foo", use_tree.to_string()); let UseTreeKind::Path(ident, alias) = &use_tree.kind else { panic!("Expected path"); }; @@ -153,7 +157,7 @@ mod tests { }; assert_eq!(visibility, ItemVisibility::Private); assert_eq!(use_tree.prefix.kind, PathKind::Plain); - assert_eq!("", use_tree.prefix.to_string()); + assert_eq!("foo as bar", use_tree.to_string()); let UseTreeKind::Path(ident, alias) = use_tree.kind else { panic!("Expected path"); }; @@ -173,7 +177,7 @@ mod tests { }; assert_eq!(visibility, ItemVisibility::Private); assert_eq!(use_tree.prefix.kind, PathKind::Crate); - assert_eq!("crate::", use_tree.prefix.to_string()); + assert_eq!("crate::foo", use_tree.to_string()); let UseTreeKind::Path(ident, alias) = use_tree.kind else { panic!("Expected path"); }; @@ -193,7 +197,7 @@ mod tests { }; assert_eq!(visibility, ItemVisibility::Private); assert_eq!(use_tree.prefix.kind, PathKind::Dep); - assert_eq!("dep::", use_tree.prefix.to_string()); + assert_eq!("dep::foo", use_tree.to_string()); let UseTreeKind::Path(ident, alias) = use_tree.kind else { panic!("Expected path"); }; @@ -213,7 +217,7 @@ mod tests { }; assert_eq!(visibility, ItemVisibility::Private); assert_eq!(use_tree.prefix.kind, PathKind::Super); - assert_eq!("super::", use_tree.prefix.to_string()); + assert_eq!("super::foo", use_tree.to_string()); let UseTreeKind::Path(ident, alias) = use_tree.kind else { panic!("Expected path"); }; @@ -233,7 +237,7 @@ mod tests { }; assert_eq!(visibility, &ItemVisibility::Private); assert_eq!(use_tree.prefix.kind, PathKind::Plain); - assert_eq!("foo", use_tree.prefix.to_string()); + assert_eq!("foo::{bar, baz}", use_tree.to_string()); let UseTreeKind::List(use_trees) = &use_tree.kind else { panic!("Expected list"); }; @@ -252,10 +256,24 @@ mod tests { }; assert_eq!(visibility, &ItemVisibility::Private); assert_eq!(use_tree.prefix.kind, PathKind::Plain); - assert_eq!("foo", use_tree.prefix.to_string()); + 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 (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Import(use_tree, visibility) = &item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, &ItemVisibility::Private); + assert_eq!("crate::{foo, bar}", use_tree.to_string()); + } } From 363dce78a6a61e274fee01fbf12578da6c8a180e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 11:42:34 -0300 Subject: [PATCH 007/229] Parse item visibility --- compiler/noirc_frontend/src/parser/parser.rs | 30 +++++++++++++++++-- .../src/parser/parser/use_tree.rs | 26 ++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 35e41ada48a..5953bfd38d0 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -123,8 +123,7 @@ impl<'a> Parser<'a> { return Some(kind); } - // TODO: visibility - let visibility = ItemVisibility::Private; + let visibility = self.parse_item_visibility(); if self.eat_keyword(Keyword::Use) { let use_tree = self.parse_use_tree(); @@ -134,6 +133,33 @@ impl<'a> Parser<'a> { None } + fn parse_item_visibility(&mut self) -> ItemVisibility { + if !self.eat_keyword(Keyword::Pub) { + return ItemVisibility::Private; + } + + if self.eat(Token::LeftParen).is_none() { + // `pub` + return ItemVisibility::Public; + } + + if !self.eat_keyword(Keyword::Crate) { + // TODO: error + // `pub(` or `pub()` + self.eat(Token::RightParen); + return ItemVisibility::Public; + } + + if self.eat(Token::RightParen).is_some() { + // `pub(crate)` + ItemVisibility::PublicCrate + } else { + // `pub(crate` + // TODO: error + ItemVisibility::PublicCrate + } + } + pub(crate) fn parse_expression(&mut self) -> Expression { todo!("Parser") } diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index ebb07ea38e7..63d93b9991f 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -145,6 +145,32 @@ mod tests { assert!(alias.is_none()); } + #[test] + fn parse_simple_pub() { + let src = "pub use foo;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Import(_, visibility) = &item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, &ItemVisibility::Public); + } + + #[test] + fn parse_simple_pub_crate() { + let src = "pub(crate) use foo;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Import(_, visibility) = &item.kind else { + panic!("Expected import"); + }; + assert_eq!(visibility, &ItemVisibility::PublicCrate); + } + #[test] fn parse_simple_with_alias() { let src = "use foo as bar;"; From e7fc2cf3d76a377bcb29935a6916507339f01594 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 14:17:57 -0300 Subject: [PATCH 008/229] Parse modules --- compiler/noirc_frontend/src/parser/parser.rs | 102 ++++++------------ .../src/parser/parser/attributes.rs | 19 ++++ .../noirc_frontend/src/parser/parser/item.rs | 56 ++++++++++ .../src/parser/parser/item_visibility.rs | 32 ++++++ .../src/parser/parser/module.rs | 90 ++++++++++++++++ .../src/parser/parser/use_tree.rs | 10 +- 6 files changed, 232 insertions(+), 77 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/item.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/item_visibility.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/module.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 5953bfd38d0..264b5ba997e 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,18 +1,18 @@ use noirc_errors::Span; use crate::{ - ast::{ - Expression, Ident, ItemVisibility, LValue, Path, Pattern, Statement, TraitBound, - UnresolvedType, - }, + ast::{Expression, Ident, LValue, Path, Pattern, Statement, TraitBound, UnresolvedType}, lexer::{Lexer, SpannedTokenResult}, token::{Keyword, SpannedToken, Token, TokenKind, Tokens}, }; -use super::{Item, ItemKind, ParsedModule, ParserError}; +use super::{ItemKind, ParsedModule, ParserError}; mod attributes; mod doc_comments; +mod item; +mod item_visibility; +mod module; mod use_tree; /// Entry function for the parser - also handles lexing internally. @@ -98,68 +98,6 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_items(&mut self) -> Vec { - let mut items = Vec::new(); - - while let Some(item) = self.parse_item() { - items.push(item); - } - - items - } - - fn parse_item(&mut self) -> Option { - let doc_comments = self.parse_outer_doc_comments(); - - let start_span = self.current_token_span; - let kind = self.parse_item_kind()?; - let span = self.span_since(start_span); - - Some(Item { kind, span, doc_comments }) - } - - fn parse_item_kind(&mut self) -> Option { - if let Some(kind) = self.parse_inner_attribute() { - return Some(kind); - } - - let visibility = self.parse_item_visibility(); - - if self.eat_keyword(Keyword::Use) { - let use_tree = self.parse_use_tree(); - return Some(ItemKind::Import(use_tree, visibility)); - } - - None - } - - fn parse_item_visibility(&mut self) -> ItemVisibility { - if !self.eat_keyword(Keyword::Pub) { - return ItemVisibility::Private; - } - - if self.eat(Token::LeftParen).is_none() { - // `pub` - return ItemVisibility::Public; - } - - if !self.eat_keyword(Keyword::Crate) { - // TODO: error - // `pub(` or `pub()` - self.eat(Token::RightParen); - return ItemVisibility::Public; - } - - if self.eat(Token::RightParen).is_some() { - // `pub(crate)` - ItemVisibility::PublicCrate - } else { - // `pub(crate` - // TODO: error - ItemVisibility::PublicCrate - } - } - pub(crate) fn parse_expression(&mut self) -> Expression { todo!("Parser") } @@ -245,7 +183,7 @@ impl<'a> Parser<'a> { } fn eat_comma(&mut self) -> bool { - self.eat(Token::Comma).is_some() + self.eat(Token::Comma) } fn eat_commas(&mut self) -> bool { @@ -260,7 +198,27 @@ impl<'a> Parser<'a> { } fn eat_semicolon(&mut self) -> bool { - self.eat(Token::Semicolon).is_some() + self.eat(Token::Semicolon) + } + + fn eat_double_colon(&mut self) -> bool { + self.eat(Token::DoubleColon) + } + + fn eat_left_paren(&mut self) -> bool { + self.eat(Token::LeftParen) + } + + fn eat_right_paren(&mut self) -> bool { + self.eat(Token::RightParen) + } + + fn eat_left_brace(&mut self) -> bool { + self.eat(Token::LeftBrace) + } + + fn eat_right_brace(&mut self) -> bool { + self.eat(Token::RightBrace) } fn eat_semicolons(&mut self) -> bool { @@ -274,13 +232,13 @@ impl<'a> Parser<'a> { } } - fn eat(&mut self, token: Token) -> Option<()> { + fn eat(&mut self, token: Token) -> bool { if self.token.token() == &token { // TODO: error self.next_token(); - Some(()) + true } else { - None + false } } diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index b365aa26879..c5e4690cc72 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,3 +1,4 @@ +use crate::token::SecondaryAttribute; use crate::token::{Attribute, Token, TokenKind}; use super::ItemKind; @@ -28,4 +29,22 @@ impl<'a> Parser<'a> { attributes } + + pub(super) fn validate_secondary_attributes( + &mut self, + attributes: Vec, + ) -> Vec { + attributes + .into_iter() + .filter_map(|attribute| { + match attribute { + Attribute::Function(..) => { + // TODO: error + None + } + Attribute::Secondary(attr) => Some(attr), + } + }) + .collect() + } } 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..9129e455de7 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -0,0 +1,56 @@ +use crate::{ + parser::{Item, ItemKind}, + token::Keyword, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_items(&mut self) -> Vec { + let mut items = Vec::new(); + + while let Some(item) = self.parse_item() { + items.push(item); + } + + items + } + + fn parse_item(&mut self) -> Option { + let doc_comments = self.parse_outer_doc_comments(); + + let start_span = self.current_token_span; + let kind = self.parse_item_kind()?; + let span = self.span_since(start_span); + + Some(Item { kind, span, doc_comments }) + } + + fn parse_item_kind(&mut self) -> Option { + if let Some(kind) = self.parse_inner_attribute() { + return Some(kind); + } + + let visibility = self.parse_item_visibility(); + let attributes = self.parse_attributes(); + + if self.eat_keyword(Keyword::Use) { + let use_tree = self.parse_use_tree(); + return Some(ItemKind::Import(use_tree, visibility)); + } + + let module_or_contract = if self.eat_keyword(Keyword::Mod) { + Some(false) + } else if self.eat_keyword(Keyword::Contract) { + Some(true) + } else { + None + }; + + if let Some(is_contract) = module_or_contract { + return Some(self.parse_module_or_contract(attributes, is_contract)); + } + + None + } +} 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..24ff89df3fa --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -0,0 +1,32 @@ +use crate::{ast::ItemVisibility, token::Keyword}; + +use super::Parser; + +impl<'a> Parser<'a> { + 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) { + // TODO: error + // `pub(` or `pub()` + self.eat_right_paren(); + return ItemVisibility::Public; + } + + if self.eat_right_paren() { + // `pub(crate)` + ItemVisibility::PublicCrate + } else { + // `pub(crate` + // TODO: error + ItemVisibility::PublicCrate + } + } +} 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..da6314f809c --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -0,0 +1,90 @@ +use crate::{ + ast::{Ident, ModuleDeclaration}, + parser::{ItemKind, ParsedSubModule}, + token::Attribute, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(super) fn parse_module_or_contract( + &mut self, + attributes: Vec, + is_contract: bool, + ) -> ItemKind { + let outer_attributes = self.validate_secondary_attributes(attributes); + + if let Some(ident) = self.eat_ident() { + if self.eat_left_brace() { + let contents = self.parse_module(); + if !self.eat_right_brace() { + // TODO: error + } + ItemKind::Submodules(ParsedSubModule { + name: ident, + contents, + outer_attributes, + is_contract, + }) + } else { + self.eat_semicolons(); + ItemKind::ModuleDecl(ModuleDeclaration { ident, outer_attributes }) + } + } else { + // TODO: error + ItemKind::ModuleDecl(ModuleDeclaration { + ident: Ident::new(String::new(), Default::default()), + outer_attributes, + }) + } + } +} + +#[cfg(test)] +mod tests { + use crate::parser::{parser::parse_program, 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); + assert!(errors.is_empty()); + 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); + assert!(errors.is_empty()); + 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); + assert!(errors.is_empty()); + 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/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 63d93b9991f..f7ee593a61b 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, Path, PathKind, PathSegment, UseTree, UseTreeKind}, - token::{Keyword, Token}, + token::Keyword, }; use super::Parser; @@ -13,7 +13,7 @@ impl<'a> Parser<'a> { let kind = self.parse_path_kind(); if kind != PathKind::Plain { - if self.eat(Token::DoubleColon).is_none() { + if !self.eat_double_colon() { // TODO: error } } @@ -36,7 +36,7 @@ impl<'a> Parser<'a> { while let Some(ident) = self.eat_ident() { let span = ident.span(); segments.push(PathSegment { ident, generics: None, span }); - if self.eat(Token::DoubleColon).is_some() { + if self.eat_double_colon() { trailing_double_colon = true; } else { trailing_double_colon = false; @@ -52,7 +52,7 @@ impl<'a> Parser<'a> { } if trailing_double_colon { - if self.eat(Token::LeftBrace).is_some() { + if self.eat_left_brace() { let mut use_trees = Vec::new(); loop { let current_span = self.current_token_span; @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { self.eat_commas(); - if self.eat(Token::RightBrace).is_some() { + if self.eat_right_brace() { break; } } From 2f3c28d9d58562e8866e7b0b6ebbf84b8acae828 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 14:20:45 -0300 Subject: [PATCH 009/229] Fix item visibility parsing issue --- .../src/parser/parser/item_visibility.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 24ff89df3fa..ac6c79f5648 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -8,7 +8,7 @@ impl<'a> Parser<'a> { return ItemVisibility::Private; } - if self.eat_left_paren() { + if !self.eat_left_paren() { // `pub` return ItemVisibility::Public; } @@ -20,13 +20,12 @@ impl<'a> Parser<'a> { return ItemVisibility::Public; } - if self.eat_right_paren() { - // `pub(crate)` - ItemVisibility::PublicCrate - } else { + if !self.eat_right_paren() { // `pub(crate` // TODO: error - ItemVisibility::PublicCrate } + + // `pub(crate)`` + ItemVisibility::PublicCrate } } From b6247ffaf829d3024445a2a2c5ddf80af3db64a1 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 15:13:51 -0300 Subject: [PATCH 010/229] Parse struct with generics --- compiler/noirc_frontend/src/ast/statement.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 37 +++++- .../src/parser/parser/generics.rs | 76 +++++++++++ .../noirc_frontend/src/parser/parser/item.rs | 6 + .../src/parser/parser/module.rs | 5 +- .../src/parser/parser/structs.rs | 123 ++++++++++++++++++ .../noirc_frontend/src/parser/parser/types.rs | 24 ++++ .../src/parser/parser/use_tree.rs | 5 +- 8 files changed, 263 insertions(+), 15 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/generics.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/structs.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/types.rs diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 04af0044fcc..034739f16f6 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -176,7 +176,7 @@ impl StatementKind { } } -#[derive(Eq, Debug, Clone)] +#[derive(Eq, Debug, Clone, Default)] pub struct Ident(pub Spanned); impl Ident { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 264b5ba997e..21e43403f5e 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,18 +1,21 @@ use noirc_errors::Span; use crate::{ - ast::{Expression, Ident, LValue, Path, Pattern, Statement, TraitBound, UnresolvedType}, + ast::{Expression, Ident, LValue, Path, Pattern, Statement, TraitBound}, lexer::{Lexer, SpannedTokenResult}, - token::{Keyword, SpannedToken, Token, TokenKind, Tokens}, + token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; use super::{ItemKind, ParsedModule, ParserError}; mod attributes; mod doc_comments; +mod generics; mod item; mod item_visibility; mod module; +mod structs; +mod types; mod use_tree; /// Entry function for the parser - also handles lexing internally. @@ -110,10 +113,6 @@ impl<'a> Parser<'a> { todo!("Parser") } - pub(crate) fn parse_type(&mut self) -> UnresolvedType { - todo!("Parser") - } - pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { todo!("Parser") } @@ -182,6 +181,20 @@ impl<'a> Parser<'a> { } } + fn eat_int_type(&mut self) -> Option { + let is_int_type = matches!(self.token.token(), Token::IntType(..)); + if is_int_type { + let token = std::mem::take(&mut self.token); + self.next_token(); + match token.into_token() { + Token::IntType(int_type) => Some(int_type), + _ => unreachable!(), + } + } else { + None + } + } + fn eat_comma(&mut self) -> bool { self.eat(Token::Comma) } @@ -201,6 +214,10 @@ impl<'a> Parser<'a> { self.eat(Token::Semicolon) } + fn eat_colon(&mut self) -> bool { + self.eat(Token::Colon) + } + fn eat_double_colon(&mut self) -> bool { self.eat(Token::DoubleColon) } @@ -221,6 +238,14 @@ impl<'a> Parser<'a> { self.eat(Token::RightBrace) } + fn eat_less(&mut self) -> bool { + self.eat(Token::Less) + } + + fn eat_greater(&mut self) -> bool { + self.eat(Token::Greater) + } + fn eat_semicolons(&mut self) -> bool { if self.eat_semicolon() { while self.eat_semicolon() { 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..6798e0e57d6 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -0,0 +1,76 @@ +use noirc_errors::Span; + +use crate::{ + ast::{ + IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, + UnresolvedTypeData, + }, + token::{Keyword, Token, TokenKind}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(super) fn parse_generics(&mut self) -> UnresolvedGenerics { + let mut generics = Vec::new(); + + if !self.eat_less() { + return generics; + } + + while let Some(generic) = self.parse_generic() { + generics.push(generic); + + self.eat_commas(); + if self.eat_greater() { + break; + } + } + + generics + } + + fn parse_generic(&mut self) -> Option { + // Check `T` + if let Some(ident) = self.eat_ident() { + return Some(UnresolvedGeneric::Variable(ident)); + } + + // Check `let N: u32` + if self.eat_keyword(Keyword::Let) { + let Some(ident) = self.eat_ident() else { + return None; + }; + + if !self.eat_colon() { + // TODO: error + return Some(UnresolvedGeneric::Numeric { ident, typ: type_u32() }); + } + + let typ = self.parse_type(); + + // TODO: error if typ isn't an integer type + + return Some(UnresolvedGeneric::Numeric { ident, typ }); + } + + // Check resolved generics + if let Some(token) = self.eat_kind(TokenKind::QuotedType) { + match token.into_token() { + Token::QuotedType(id) => { + return Some(UnresolvedGeneric::Resolved(id, self.previous_token_span)); + } + _ => unreachable!(), + } + } + + None + } +} + +fn type_u32() -> UnresolvedType { + UnresolvedType { + typ: UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), + span: Span::default(), + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 9129e455de7..135f4f8ba36 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -34,6 +34,8 @@ impl<'a> Parser<'a> { let visibility = self.parse_item_visibility(); let attributes = self.parse_attributes(); + let start_span = self.current_token_span; + if self.eat_keyword(Keyword::Use) { let use_tree = self.parse_use_tree(); return Some(ItemKind::Import(use_tree, visibility)); @@ -47,6 +49,10 @@ impl<'a> Parser<'a> { None }; + if self.eat_keyword(Keyword::Struct) { + return Some(ItemKind::Struct(self.parse_struct(attributes, visibility, start_span))); + } + if let Some(is_contract) = module_or_contract { return Some(self.parse_module_or_contract(attributes, is_contract)); } diff --git a/compiler/noirc_frontend/src/parser/parser/module.rs b/compiler/noirc_frontend/src/parser/parser/module.rs index da6314f809c..853a5f933e0 100644 --- a/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -32,10 +32,7 @@ impl<'a> Parser<'a> { } } else { // TODO: error - ItemKind::ModuleDecl(ModuleDeclaration { - ident: Ident::new(String::new(), Default::default()), - outer_attributes, - }) + ItemKind::ModuleDecl(ModuleDeclaration { ident: Ident::default(), outer_attributes }) } } } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs new file mode 100644 index 00000000000..cbfd5063f2f --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -0,0 +1,123 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Ident, ItemVisibility, NoirStruct, UnresolvedGenerics}, + token::{Attribute, SecondaryAttribute}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_struct( + &mut self, + attributes: Vec, + visibility: ItemVisibility, + start_span: Span, + ) -> NoirStruct { + let attributes = self.validate_secondary_attributes(attributes); + + let Some(name) = self.eat_ident() else { + // TODO: error + return self.empty_struct( + Ident::default(), + attributes, + visibility, + Vec::new(), + start_span, + ); + }; + + let generics = self.parse_generics(); + + if !self.eat_left_brace() { + // TODO: error + return self.empty_struct( + Ident::default(), + attributes, + visibility, + generics, + start_span, + ); + } + + // TODO: fields + + if !self.eat_right_brace() { + // TODO: error + } + + self.empty_struct(name, attributes, visibility, generics, start_span) + } + + 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 tests { + use crate::{ + ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parser::{parser::parse_program, ItemKind}, + }; + + #[test] + fn parse_empty_struct() { + let src = "struct Foo {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Struct(noir_struct) = &item.kind else { + panic!("Expected import"); + }; + 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Struct(mut noir_struct) = item.kind else { + panic!("Expected import"); + }; + 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) + ) + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs new file mode 100644 index 00000000000..cc66bd8a0f7 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -0,0 +1,24 @@ +use crate::ast::{UnresolvedType, UnresolvedTypeData}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_type(&mut self) -> UnresolvedType { + let start_span = self.current_token_span; + + if let Some(int_type) = self.eat_int_type() { + let typ = match UnresolvedTypeData::from_int_token(int_type) { + Ok(typ) => typ, + Err(_) => { + // TODO: error + UnresolvedTypeData::Error + } + }; + return UnresolvedType { typ, span: self.span_since(start_span) }; + } + + // TODO: parse more types + + UnresolvedType { typ: UnresolvedTypeData::Error, span: start_span } + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index f7ee593a61b..d3d9cca2985 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -86,10 +86,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path) -> UseTree { if prefix.segments.is_empty() { // TODO: error - UseTree { - prefix, - kind: UseTreeKind::Path(Ident::new(String::new(), Span::default()), None), - } + UseTree { prefix, kind: UseTreeKind::Path(Ident::default(), None) } } else { let ident = prefix.segments.pop().unwrap().ident; if self.eat_keyword(Keyword::As) { From 7d95360c9c9cc7c4729e4810fda5c512764e9269 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 15:30:23 -0300 Subject: [PATCH 011/229] Parse struct fields --- .../src/parser/parser/structs.rs | 61 +++++++++++++++++-- .../noirc_frontend/src/parser/parser/types.rs | 24 +++++--- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index cbfd5063f2f..051d87ea303 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -1,7 +1,7 @@ use noirc_errors::Span; use crate::{ - ast::{Ident, ItemVisibility, NoirStruct, UnresolvedGenerics}, + ast::{Documented, Ident, ItemVisibility, NoirStruct, StructField, UnresolvedGenerics}, token::{Attribute, SecondaryAttribute}, }; @@ -40,13 +40,39 @@ impl<'a> Parser<'a> { ); } - // TODO: fields + let mut fields = Vec::new(); + + loop { + let doc_comments = self.parse_outer_doc_comments(); + + let Some(name) = self.eat_ident() else { + // TODO: error if there are doc comments + break; + }; + + if !self.eat_colon() { + // TODO: error + } + + let typ = self.parse_type(); + + fields.push(Documented::new(StructField { name, typ }, doc_comments)); + + self.eat_commas(); + } if !self.eat_right_brace() { // TODO: error } - self.empty_struct(name, attributes, visibility, generics, start_span) + NoirStruct { + name, + attributes, + visibility, + generics, + fields, + span: self.span_since(start_span), + } } fn empty_struct( @@ -83,7 +109,7 @@ mod tests { assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Struct(noir_struct) = &item.kind else { - panic!("Expected import"); + panic!("Expected struct"); }; assert_eq!("Foo", noir_struct.name.to_string()); assert!(noir_struct.fields.is_empty()); @@ -98,7 +124,7 @@ mod tests { assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Struct(mut noir_struct) = item.kind else { - panic!("Expected import"); + panic!("Expected struct"); }; assert_eq!("Foo", noir_struct.name.to_string()); assert!(noir_struct.fields.is_empty()); @@ -120,4 +146,29 @@ mod tests { UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) ) } + + #[test] + fn parse_struct_with_fields() { + let src = "struct Foo { x: i32, y: Field }"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Struct(mut noir_struct) = item.kind else { + panic!("Expected struct"); + }; + 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)); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index cc66bd8a0f7..8d986608e82 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,4 +1,7 @@ -use crate::ast::{UnresolvedType, UnresolvedTypeData}; +use crate::{ + ast::{UnresolvedType, UnresolvedTypeData}, + token::Keyword, +}; use super::Parser; @@ -6,19 +9,22 @@ impl<'a> Parser<'a> { pub(crate) fn parse_type(&mut self) -> UnresolvedType { let start_span = self.current_token_span; - if let Some(int_type) = self.eat_int_type() { - let typ = match UnresolvedTypeData::from_int_token(int_type) { + // TODO: parse more types + + let typ = if self.eat_keyword(Keyword::Field) { + UnresolvedTypeData::FieldElement + } else if let Some(int_type) = self.eat_int_type() { + match UnresolvedTypeData::from_int_token(int_type) { Ok(typ) => typ, Err(_) => { // TODO: error UnresolvedTypeData::Error } - }; - return UnresolvedType { typ, span: self.span_since(start_span) }; - } - - // TODO: parse more types + } + } else { + return UnresolvedType { typ: UnresolvedTypeData::Error, span: start_span }; + }; - UnresolvedType { typ: UnresolvedTypeData::Error, span: start_span } + UnresolvedType { typ, span: self.span_since(start_span) } } } From 5720460f04802c92b265da50cd457856de9cfbdd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 15:38:15 -0300 Subject: [PATCH 012/229] Fix some bugs --- compiler/noirc_frontend/src/parser/parser.rs | 1 - .../src/parser/parser/attributes.rs | 1 - .../src/parser/parser/doc_comments.rs | 2 -- .../src/parser/parser/structs.rs | 22 +++++++++++++------ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 21e43403f5e..a7dfc9a38ab 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -259,7 +259,6 @@ impl<'a> Parser<'a> { fn eat(&mut self, token: Token) -> bool { if self.token.token() == &token { - // TODO: error self.next_token(); true } else { diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index c5e4690cc72..db84a39e28e 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -21,7 +21,6 @@ impl<'a> Parser<'a> { match token.into_token() { Token::Attribute(attribute) => { attributes.push(attribute.clone()); - self.next_token(); } _ => unreachable!(), } diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index f9dd95d75f2..098d872fde8 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -11,7 +11,6 @@ impl<'a> Parser<'a> { Token::LineComment(comment, Some(DocStyle::Inner)) | Token::BlockComment(comment, Some(DocStyle::Inner)) => { comments.push(comment); - self.next_token(); } _ => unreachable!(), } @@ -28,7 +27,6 @@ impl<'a> Parser<'a> { Token::LineComment(comment, Some(DocStyle::Outer)) | Token::BlockComment(comment, Some(DocStyle::Outer)) => { comments.push(comment); - self.next_token(); } _ => unreachable!(), } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 051d87ea303..43950d83eb4 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -31,13 +31,7 @@ impl<'a> Parser<'a> { if !self.eat_left_brace() { // TODO: error - return self.empty_struct( - Ident::default(), - attributes, - visibility, - generics, - start_span, - ); + return self.empty_struct(name, attributes, visibility, generics, start_span); } let mut fields = Vec::new(); @@ -171,4 +165,18 @@ mod tests { 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); + assert!(errors.is_empty()); + 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()); + } } From 583a462c4b38db941e64b76008d44b4855dc2a96 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 16:06:12 -0300 Subject: [PATCH 013/229] Parse globals --- compiler/noirc_frontend/src/parser/parser.rs | 27 +++- .../src/parser/parser/expression.rs | 21 +++ .../src/parser/parser/global.rs | 136 ++++++++++++++++++ .../noirc_frontend/src/parser/parser/item.rs | 9 ++ .../noirc_frontend/src/parser/parser/types.rs | 10 ++ 5 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/expression.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/global.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index a7dfc9a38ab..d3cd0cd28b1 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,7 +1,8 @@ +use acvm::FieldElement; use noirc_errors::Span; use crate::{ - ast::{Expression, Ident, LValue, Path, Pattern, Statement, TraitBound}, + ast::{Ident, LValue, Path, Pattern, Statement, TraitBound}, lexer::{Lexer, SpannedTokenResult}, token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; @@ -10,7 +11,9 @@ use super::{ItemKind, ParsedModule, ParserError}; mod attributes; mod doc_comments; +mod expression; mod generics; +mod global; mod item; mod item_visibility; mod module; @@ -101,10 +104,6 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_expression(&mut self) -> Expression { - todo!("Parser") - } - pub(crate) fn parse_path_no_turbofish(&mut self) -> Path { todo!("Parser") } @@ -195,6 +194,20 @@ impl<'a> Parser<'a> { } } + fn eat_int(&mut self) -> Option { + let is_int = matches!(self.token.token(), Token::Int(..)); + if is_int { + let token = std::mem::take(&mut self.token); + self.next_token(); + match token.into_token() { + Token::Int(int) => Some(int), + _ => unreachable!(), + } + } else { + None + } + } + fn eat_comma(&mut self) -> bool { self.eat(Token::Comma) } @@ -246,6 +259,10 @@ impl<'a> Parser<'a> { self.eat(Token::Greater) } + fn eat_assign(&mut self) -> bool { + self.eat(Token::Assign) + } + fn eat_semicolons(&mut self) -> bool { if self.eat_semicolon() { while self.eat_semicolon() { 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..87b19990e2a --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -0,0 +1,21 @@ +use noirc_errors::Span; + +use crate::ast::{Expression, ExpressionKind}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_expression(&mut self) -> Expression { + // TODO: parse other expressions + + let start_span = self.current_token_span; + + let kind = if let Some(int) = self.eat_int() { + ExpressionKind::integer(int) + } else { + return Expression { kind: ExpressionKind::Error, span: Span::default() }; + }; + + Expression { kind, span: self.span_since(start_span) } + } +} 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..71b8f16ccd7 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -0,0 +1,136 @@ +use noirc_errors::Span; + +use crate::{ + ast::{ + Expression, ExpressionKind, Ident, LetStatement, Pattern, UnresolvedType, + UnresolvedTypeData, + }, + token::Attribute, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_global( + &mut self, + attributes: Vec, + comptime: bool, + mutable: bool, + ) -> LetStatement { + 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: 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() + } else { + // TODO: error + Expression { kind: ExpressionKind::Error, span: Span::default() } + }; + + 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, Pattern, Signedness, UnresolvedTypeData}, + parser::{parser::parse_program, ItemKind}, + }; + + #[test] + fn parse_global_no_type_annotation() { + let src = "global foo = 1;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Global(let_statement) = &item.kind else { + panic!("Expected global"); + }; + 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); + } + + #[test] + fn parse_global_with_type_annotation() { + let src = "global foo: i32 = 1;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Global(let_statement) = &item.kind else { + panic!("Expected global"); + }; + 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 (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Global(let_statement) = &item.kind else { + panic!("Expected global"); + }; + assert!(let_statement.comptime); + } + + #[test] + fn parse_mutable_global() { + let src = "mut global foo: i32 = 1;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Global(let_statement) = &item.kind else { + panic!("Expected global"); + }; + 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()); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 135f4f8ba36..a77167fc9f6 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -36,7 +36,11 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; + let comptime = self.eat_keyword(Keyword::Comptime); + let mutable = self.eat_keyword(Keyword::Mut); + if self.eat_keyword(Keyword::Use) { + // TODO: error if there's comptime or mutable let use_tree = self.parse_use_tree(); return Some(ItemKind::Import(use_tree, visibility)); } @@ -50,9 +54,14 @@ impl<'a> Parser<'a> { }; if self.eat_keyword(Keyword::Struct) { + // TODO: error if there's comptime or mutable return Some(ItemKind::Struct(self.parse_struct(attributes, visibility, start_span))); } + if self.eat_keyword(Keyword::Global) { + return Some(ItemKind::Global(self.parse_global(attributes, comptime, mutable))); + } + if let Some(is_contract) = module_or_contract { return Some(self.parse_module_or_contract(attributes, is_contract)); } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 8d986608e82..704996a9375 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,3 +1,5 @@ +use noirc_errors::Span; + use crate::{ ast::{UnresolvedType, UnresolvedTypeData}, token::Keyword, @@ -27,4 +29,12 @@ impl<'a> Parser<'a> { UnresolvedType { typ, span: self.span_since(start_span) } } + + pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { + if self.eat_colon() { + self.parse_type() + } else { + UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + } + } } From 93d0c22f8d87523fcce072eaba03255c9905b2be Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 16:06:16 -0300 Subject: [PATCH 014/229] Handle struct without name in document symbol --- tooling/lsp/src/requests/document_symbol.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tooling/lsp/src/requests/document_symbol.rs b/tooling/lsp/src/requests/document_symbol.rs index 6c41f4dc2e5..aedf35e9e62 100644 --- a/tooling/lsp/src/requests/document_symbol.rs +++ b/tooling/lsp/src/requests/document_symbol.rs @@ -162,6 +162,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; }; From 768b4fb22c9bbb6e254d400b7d835cc6d25c82f2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 16:07:16 -0300 Subject: [PATCH 015/229] Add pending error --- compiler/noirc_frontend/src/parser/parser/global.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index 71b8f16ccd7..7167f9c0d88 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -17,6 +17,8 @@ impl<'a> Parser<'a> { comptime: bool, mutable: bool, ) -> LetStatement { + // TODO: error if mutable but not comptime + let attributes = self.validate_secondary_attributes(attributes); let Some(ident) = self.eat_ident() else { From 26a7a61b5007a9c91fa8ff985e4fdb63c6abbfcd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 16:09:20 -0300 Subject: [PATCH 016/229] Some clippy --- compiler/noirc_frontend/src/lexer/token.rs | 9 ++------- compiler/noirc_frontend/src/parser/parser.rs | 8 +------- compiler/noirc_frontend/src/parser/parser/global.rs | 2 +- compiler/noirc_frontend/src/parser/parser/use_tree.rs | 6 ++---- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 5f15f445401..82ea1469edb 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -124,7 +124,7 @@ pub enum BorrowedToken<'input> { Invalid(char), } -#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] +#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord, Default)] pub enum Token { Ident(String), Int(FieldElement), @@ -223,6 +223,7 @@ pub enum Token { /// $ DollarSign, #[allow(clippy::upper_case_acronyms)] + #[default] EOF, Whitespace(String), @@ -334,12 +335,6 @@ impl<'a> From<&'a SpannedToken> for &'a Token { } } -impl Default for Token { - fn default() -> Self { - Token::EOF - } -} - impl SpannedToken { pub fn new(token: Token, span: Span) -> SpannedToken { SpannedToken(Spanned::from(span, token)) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index d3cd0cd28b1..4bd6d6db310 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -56,13 +56,7 @@ impl<'a> TokenStream<'a> { fn next(&mut self) -> Option { match self { TokenStream::Lexer(lexer) => lexer.next(), - TokenStream::Tokens(tokens) => { - if let Some(token) = tokens.0.pop() { - Some(Ok(token)) - } else { - None - } - } + TokenStream::Tokens(tokens) => tokens.0.pop().map(Ok), } } } diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index 7167f9c0d88..27ba1d134bc 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -30,7 +30,7 @@ impl<'a> Parser<'a> { span: Span::default(), }, expression: Expression { kind: ExpressionKind::Error, span: Span::default() }, - attributes: attributes, + attributes, comptime, }; }; diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index d3d9cca2985..489bf44f47e 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -12,10 +12,8 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let kind = self.parse_path_kind(); - if kind != PathKind::Plain { - if !self.eat_double_colon() { - // TODO: error - } + if kind != PathKind::Plain && !self.eat_double_colon() { + // TODO: error } let use_tree = self.parse_use_tree_without_kind(start_span, kind); From df84945a7c60bdd557864181b005d0a2cc9c7f05 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 17:10:29 -0300 Subject: [PATCH 017/229] Tiny refactor --- .../noirc_frontend/src/parser/parser/item.rs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index a77167fc9f6..e3f8da88eed 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -45,13 +45,10 @@ impl<'a> Parser<'a> { return Some(ItemKind::Import(use_tree, visibility)); } - let module_or_contract = if self.eat_keyword(Keyword::Mod) { - Some(false) - } else if self.eat_keyword(Keyword::Contract) { - Some(true) - } else { - None - }; + if let Some(is_contract) = self.eat_mod_or_contract() { + // TODO: error if there's comptime or mutable + return Some(self.parse_module_or_contract(attributes, is_contract)); + } if self.eat_keyword(Keyword::Struct) { // TODO: error if there's comptime or mutable @@ -62,10 +59,16 @@ impl<'a> Parser<'a> { return Some(ItemKind::Global(self.parse_global(attributes, comptime, mutable))); } - if let Some(is_contract) = module_or_contract { - return Some(self.parse_module_or_contract(attributes, is_contract)); - } - 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 + } + } } From f38b202e97fcb8b52b6d7ba48d5c342634827f4a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 17:21:25 -0300 Subject: [PATCH 018/229] Parse type alias --- compiler/noirc_frontend/src/parser/parser.rs | 1 + .../noirc_frontend/src/parser/parser/item.rs | 4 + .../src/parser/parser/type_alias.rs | 74 +++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 compiler/noirc_frontend/src/parser/parser/type_alias.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 4bd6d6db310..009213c92ba 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -18,6 +18,7 @@ mod item; mod item_visibility; mod module; mod structs; +mod type_alias; mod types; mod use_tree; diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index e3f8da88eed..6c3a0ce94f7 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -59,6 +59,10 @@ impl<'a> Parser<'a> { return Some(ItemKind::Global(self.parse_global(attributes, comptime, mutable))); } + if self.eat_keyword(Keyword::Type) { + return Some(ItemKind::TypeAlias(self.parse_type_alias(start_span))); + } + None } 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..03ff8a37301 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -0,0 +1,74 @@ +use noirc_errors::Span; + +use crate::ast::{Ident, NoirTypeAlias, UnresolvedType, UnresolvedTypeData}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_type_alias(&mut self, start_span: Span) -> NoirTypeAlias { + let Some(name) = self.eat_ident() else { + // TODO: error + return NoirTypeAlias { + 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.eat_semicolons(); + + // TODO: error + return NoirTypeAlias { + name, + generics, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + span: self.span_since(start_span), + }; + } + + let typ = self.parse_type(); + + NoirTypeAlias { name, generics, typ, span: self.span_since(start_span) } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::UnresolvedTypeData, + parser::{parser::parse_program, ItemKind}, + }; + + #[test] + fn parse_type_alias_no_generics() { + let src = "type Foo = Field;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::TypeAlias(alias) = &item.kind else { + panic!("Expected global"); + }; + 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 (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::TypeAlias(alias) = &item.kind else { + panic!("Expected global"); + }; + assert_eq!("Foo", alias.name.to_string()); + assert_eq!(alias.generics.len(), 1); + } +} From e70d532cb5c2086d7efe53c545253adb17070a0b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 25 Sep 2024 18:11:23 -0300 Subject: [PATCH 019/229] Parse a bit of functions --- compiler/noirc_frontend/src/parser/parser.rs | 8 +- .../src/parser/parser/expression.rs | 10 +- .../src/parser/parser/function.rs | 328 ++++++++++++++++++ .../noirc_frontend/src/parser/parser/item.rs | 29 +- .../src/parser/parser/pattern.rs | 14 + .../src/parser/parser/type_alias.rs | 2 +- 6 files changed, 380 insertions(+), 11 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/function.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/pattern.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 009213c92ba..07d390a5891 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -2,7 +2,7 @@ use acvm::FieldElement; use noirc_errors::Span; use crate::{ - ast::{Ident, LValue, Path, Pattern, Statement, TraitBound}, + ast::{Ident, LValue, Path, Statement, TraitBound}, lexer::{Lexer, SpannedTokenResult}, token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; @@ -12,11 +12,13 @@ use super::{ItemKind, ParsedModule, ParserError}; mod attributes; mod doc_comments; mod expression; +mod function; mod generics; mod global; mod item; mod item_visibility; mod module; +mod pattern; mod structs; mod type_alias; mod types; @@ -103,10 +105,6 @@ impl<'a> Parser<'a> { todo!("Parser") } - pub(crate) fn parse_pattern(&mut self) -> Pattern { - todo!("Parser") - } - pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { todo!("Parser") } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 87b19990e2a..cb74f06be8e 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,6 +1,6 @@ use noirc_errors::Span; -use crate::ast::{Expression, ExpressionKind}; +use crate::ast::{BlockExpression, Expression, ExpressionKind}; use super::Parser; @@ -18,4 +18,12 @@ impl<'a> Parser<'a> { Expression { kind, span: self.span_since(start_span) } } + + pub(super) fn parse_block_expression(&mut self) -> BlockExpression { + self.eat_left_brace(); + // TODO: parse statements + self.eat_right_brace(); + + BlockExpression { statements: Vec::new() } + } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs new file mode 100644 index 00000000000..f3603f5bdec --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -0,0 +1,328 @@ +use acvm::AcirField; +use noirc_errors::Span; + +use crate::{ + ast::{ + BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, + NoirFunction, Param, UnresolvedType, UnresolvedTypeData, Visibility, + }, + token::{Attribute, Attributes, Keyword, Token}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_function( + &mut self, + attributes: Vec, + visibility: ItemVisibility, + is_comptime: bool, + is_unconstrained: bool, + allow_self: bool, + start_span: Span, + ) -> NoirFunction { + self.parse_function_definition( + attributes, + visibility, + is_comptime, + is_unconstrained, + allow_self, + start_span, + ) + .into() + } + + pub(crate) fn parse_function_definition( + &mut self, + attributes: Vec, + visibility: ItemVisibility, + is_comptime: bool, + is_unconstrained: bool, + allow_self: bool, + start_span: Span, + ) -> FunctionDefinition { + let attributes = self.validate_attributes(attributes); + + let Some(name) = self.eat_ident() else { + // TODO: error + return FunctionDefinition { + name: Ident::default(), + attributes, + is_unconstrained, + is_comptime, + visibility, + generics: Vec::new(), + parameters: Vec::new(), + body: empty_body(), + span: start_span, + where_clause: Vec::new(), + return_type: FunctionReturnType::Default(Span::default()), + return_visibility: Visibility::Private, + }; + }; + + let generics = self.parse_generics(); + + if !self.eat_left_paren() { + // TODO: error + return FunctionDefinition { + name, + attributes, + is_unconstrained, + is_comptime, + visibility, + generics, + parameters: Vec::new(), + body: empty_body(), + span: self.span_since(start_span), + where_clause: Vec::new(), + return_type: FunctionReturnType::Default(Span::default()), + return_visibility: Visibility::Private, + }; + } + + let parameters = self.parse_function_parameters(allow_self); + + // TODO: parse return type + + // TODO: parse where clause + + let body = if let Token::LeftBrace = self.token.token() { + self.parse_block_expression() + } else { + empty_body() + }; + + FunctionDefinition { + name, + attributes, + is_unconstrained, + is_comptime, + visibility, + generics, + parameters, + body, + span: start_span, + where_clause: Vec::new(), + return_type: FunctionReturnType::Default(Span::default()), + return_visibility: Visibility::Private, + } + } + + fn parse_function_parameters(&mut self, allow_self: bool) -> Vec { + let mut parameters = Vec::new(); + + if self.eat_right_paren() { + return parameters; + } + + loop { + let start_span = self.current_token_span; + + let pattern = self.parse_pattern(); + + // Check if the parser advanced + if self.current_token_span == start_span { + if !self.eat_right_paren() { + // TODO: error + } + + break; + } + + if self.eat_colon() { + let visibility = self.parse_visibility(); + + let typ = self.parse_type(); + parameters.push(Param { + visibility, + pattern, + typ, + span: self.span_since(start_span), + }); + } else { + // TODO: error + parameters.push(Param { + visibility: Visibility::Private, + pattern, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + span: self.span_since(start_span), + }); + } + + self.eat_commas(); + + if self.eat_right_paren() { + break; + } + } + + parameters + } + + 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() { + if !self.eat_right_paren() { + // TODO: error + } + + let id = int.to_u128() as u32; + return Visibility::CallData(id); + } else { + // TODO: error + if !self.eat_right_paren() { + // TODO: error + } + return Visibility::CallData(0); + } + } else { + // TODO: error + return Visibility::CallData(0); + } + } + + Visibility::Private + } + + fn validate_attributes(&mut self, attributes: Vec) -> Attributes { + let mut primary = None; + let mut secondary = Vec::new(); + + for attribute in attributes { + match attribute { + Attribute::Function(attr) => { + if primary.is_some() { + // TODO: err + } + primary = Some(attr); + } + Attribute::Secondary(attr) => secondary.push(attr), + } + } + + Attributes { function: primary, secondary } + } +} + +fn empty_body() -> BlockExpression { + BlockExpression { statements: Vec::new() } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::Visibility, + parser::{parser::parse_program, ItemKind}, + }; + + #[test] + fn parse_simple_function() { + let src = "fn foo() {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + 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()); + assert!(noir_function.def.parameters.is_empty()); + assert!(noir_function.def.generics.is_empty()); + } + + #[test] + fn parse_function_with_generics() { + let src = "fn foo() {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Function(noir_function) = &item.kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.def.generics.len(), 1); + } + + #[test] + fn parse_function_with_arguments() { + let src = "fn foo(x: Field, y: Field) {}"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(mut noir_function) = item.kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.def.parameters.len(), 2); + + 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); + + 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 parse_function_with_argument_pub_visibility() { + let src = "fn foo(x: pub Field) {}"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(mut noir_function) = item.kind else { + panic!("Expected function"); + }; + 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_with_argument_return_data_visibility() { + let src = "fn foo(x: return_data Field) {}"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(mut noir_function) = item.kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.def.parameters.len(), 1); + + 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(mut noir_function) = item.kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.def.parameters.len(), 1); + + let param = noir_function.def.parameters.remove(0); + assert_eq!(param.visibility, Visibility::CallData(42)); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 6c3a0ce94f7..47317661c9e 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -31,38 +31,59 @@ impl<'a> Parser<'a> { return Some(kind); } - let visibility = self.parse_item_visibility(); let attributes = self.parse_attributes(); + // Functions allow `unconstrained` before their visibility + // so we check that before and after the visibility + let mut unconstrained = self.eat_keyword(Keyword::Unconstrained); + + let visibility = self.parse_item_visibility(); + let start_span = self.current_token_span; + unconstrained = unconstrained || self.eat_keyword(Keyword::Unconstrained); + let comptime = self.eat_keyword(Keyword::Comptime); let mutable = self.eat_keyword(Keyword::Mut); if self.eat_keyword(Keyword::Use) { - // TODO: error if there's comptime or mutable + // TODO: error if there's comptime, mutable or unconstrained let use_tree = self.parse_use_tree(); return Some(ItemKind::Import(use_tree, visibility)); } if let Some(is_contract) = self.eat_mod_or_contract() { - // TODO: error if there's comptime or mutable + // TODO: error if there's comptime, mutable or unconstrained return Some(self.parse_module_or_contract(attributes, is_contract)); } if self.eat_keyword(Keyword::Struct) { - // TODO: error if there's comptime or mutable + // TODO: error if there's comptime or mutable or unconstrained return Some(ItemKind::Struct(self.parse_struct(attributes, visibility, start_span))); } if self.eat_keyword(Keyword::Global) { + // TODO: error if there's unconstrained return Some(ItemKind::Global(self.parse_global(attributes, comptime, mutable))); } if self.eat_keyword(Keyword::Type) { + // TODO: error if there's comptime or mutable or unconstrained return Some(ItemKind::TypeAlias(self.parse_type_alias(start_span))); } + if self.eat_keyword(Keyword::Fn) { + // TODO: error if there's mutable + return Some(ItemKind::Function(self.parse_function( + attributes, + visibility, + comptime, + unconstrained, + false, // allow_self + start_span, + ))); + } + None } 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..443e6b3a331 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -0,0 +1,14 @@ +use crate::ast::Pattern; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_pattern(&mut self) -> Pattern { + if let Some(ident) = self.eat_ident() { + return Pattern::Identifier(ident); + } + + // TODO: parse other patterns + todo!("Parser") + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 03ff8a37301..e551b865544 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -66,7 +66,7 @@ mod tests { assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::TypeAlias(alias) = &item.kind else { - panic!("Expected global"); + panic!("Expected type alias"); }; assert_eq!("Foo", alias.name.to_string()); assert_eq!(alias.generics.len(), 1); From 99a55035aa0c83b7e534ef9958f895ad59f2625b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 06:33:07 -0300 Subject: [PATCH 020/229] Add some more tests --- compiler/noirc_frontend/src/parser/parser.rs | 6 ++- .../src/parser/parser/attributes.rs | 37 ++++++++++++++++-- .../src/parser/parser/doc_comments.rs | 23 +++++++++++ .../src/parser/parser/expression.rs | 19 ++++++++++ .../src/parser/parser/generics.rs | 38 +++++++++++++++++++ .../noirc_frontend/src/parser/parser/item.rs | 2 +- .../src/parser/parser/item_visibility.rs | 26 +++++++++++++ .../src/parser/parser/pattern.rs | 14 +++++++ .../noirc_frontend/src/parser/parser/types.rs | 26 +++++++++++++ 9 files changed, 185 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 07d390a5891..082ed5553c4 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -7,7 +7,7 @@ use crate::{ token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; -use super::{ItemKind, ParsedModule, ParserError}; +use super::{ParsedModule, ParserError}; mod attributes; mod doc_comments; @@ -82,6 +82,10 @@ impl<'a> Parser<'a> { Self::new(TokenStream::Tokens(tokens)) } + pub fn for_str(str: &'a str) -> Self { + Self::for_lexer(Lexer::new(str)) + } + fn new(tokens: TokenStream<'a>) -> Self { let mut parser = Self { errors: Vec::new(), diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index db84a39e28e..81b0a8ba730 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,15 +1,13 @@ use crate::token::SecondaryAttribute; use crate::token::{Attribute, Token, TokenKind}; -use super::ItemKind; - use super::Parser; impl<'a> Parser<'a> { - pub(super) fn parse_inner_attribute(&mut self) -> Option { + pub(super) fn parse_inner_attribute(&mut self) -> Option { let token = self.eat_kind(TokenKind::InnerAttribute)?; match token.into_token() { - Token::InnerAttribute(attribute) => Some(ItemKind::InnerAttribute(attribute)), + Token::InnerAttribute(attribute) => Some(attribute), _ => unreachable!(), } } @@ -47,3 +45,34 @@ impl<'a> Parser<'a> { .collect() } } + +#[cfg(test)] +mod tests { + use crate::{ + parser::Parser, + token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, + }; + + #[test] + fn parses_inner_attribute() { + let src = "#![hello]"; + let Some(SecondaryAttribute::Custom(custom)) = Parser::for_str(src).parse_inner_attribute() + else { + panic!("Expected inner custom attribute"); + }; + assert_eq!(custom.contents, "hello"); + } + + #[test] + fn parses_attributes() { + let src = "#[test] #[deprecated]"; + let mut attributes = Parser::for_str(src).parse_attributes(); + assert_eq!(attributes.len(), 2); + + let attr = attributes.remove(0); + assert!(matches!(attr, Attribute::Function(FunctionAttribute::Test(TestScope::None)))); + + 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 098d872fde8..3c46e881a42 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -35,3 +35,26 @@ impl<'a> Parser<'a> { comments } } + +#[cfg(test)] +mod tests { + use crate::parser::Parser; + + #[test] + fn parses_inner_doc_comments() { + let src = "//! Hello\n//! World"; + let comments = Parser::for_str(src).parse_inner_doc_comments(); + 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 comments = Parser::for_str(src).parse_outer_doc_comments(); + 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 index cb74f06be8e..43e7cb02fa8 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -27,3 +27,22 @@ impl<'a> Parser<'a> { BlockExpression { statements: Vec::new() } } } + +#[cfg(test)] +mod tests { + use crate::{ + ast::{ExpressionKind, Literal}, + parser::Parser, + }; + + #[test] + fn parses_integer_literal() { + let src = "42"; + let expr = Parser::for_str(src).parse_expression(); + let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + panic!("Expected integer literal"); + }; + assert_eq!(field, 42_u128.into()); + assert!(!negative); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 6798e0e57d6..00a1cf2249b 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -74,3 +74,41 @@ fn type_u32() -> UnresolvedType { span: Span::default(), } } + +#[cfg(test)] +mod tests { + use crate::{ + ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parser::Parser, + }; + + #[test] + fn parses_no_generics() { + let src = "1"; + let generics = Parser::for_str(src).parse_generics(); + assert!(generics.is_empty()); + } + + #[test] + fn parses_generics() { + let src = ""; + let mut generics = Parser::for_str(src).parse_generics(); + 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) + ) + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 47317661c9e..c7a6a22726a 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { fn parse_item_kind(&mut self) -> Option { if let Some(kind) = self.parse_inner_attribute() { - return Some(kind); + return Some(ItemKind::InnerAttribute(kind)); } let attributes = self.parse_attributes(); diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index ac6c79f5648..af21fae8830 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -29,3 +29,29 @@ impl<'a> Parser<'a> { ItemVisibility::PublicCrate } } + +#[cfg(test)] +mod tests { + use crate::{ast::ItemVisibility, parser::Parser}; + + #[test] + fn parses_private_visibility() { + let src = "("; + let visibility = Parser::for_str(src).parse_item_visibility(); + assert_eq!(visibility, ItemVisibility::Private); + } + + #[test] + fn parses_public_visibility() { + let src = "pub"; + let visibility = Parser::for_str(src).parse_item_visibility(); + assert_eq!(visibility, ItemVisibility::Public); + } + + #[test] + fn parses_public_crate_visibility() { + let src = "pub(crate)"; + let visibility = Parser::for_str(src).parse_item_visibility(); + assert_eq!(visibility, ItemVisibility::PublicCrate); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 443e6b3a331..ada2ee0c1d2 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -12,3 +12,17 @@ impl<'a> Parser<'a> { todo!("Parser") } } + +#[cfg(test)] +mod tests { + + use crate::{ast::Pattern, parser::Parser}; + + #[test] + fn parses_identifier_pattern() { + let src = "foo"; + let typ = Parser::for_str(src).parse_pattern(); + let Pattern::Identifier(ident) = typ else { panic!("Expected an identifier pattern") }; + assert_eq!(ident.to_string(), "foo"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 704996a9375..eb69d3fdd04 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -38,3 +38,29 @@ impl<'a> Parser<'a> { } } } + +#[cfg(test)] +mod tests { + + use crate::{ + ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, + parser::Parser, + }; + + #[test] + fn parses_int_type() { + let src = "u32"; + let typ = Parser::for_str(src).parse_type(); + assert!(matches!( + typ.typ, + UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) + )); + } + + #[test] + fn parses_field_type() { + let src = "Field"; + let typ = Parser::for_str(src).parse_type(); + assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + } +} From 4ad2372c84d25713d84840567c69d28ec908141f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 06:35:43 -0300 Subject: [PATCH 021/229] Refactor --- .../src/parser/parser/function.rs | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index f3603f5bdec..3a8195ad97f 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -45,42 +45,16 @@ impl<'a> Parser<'a> { let Some(name) = self.eat_ident() else { // TODO: error - return FunctionDefinition { - name: Ident::default(), + return empty_fuction( attributes, is_unconstrained, is_comptime, visibility, - generics: Vec::new(), - parameters: Vec::new(), - body: empty_body(), - span: start_span, - where_clause: Vec::new(), - return_type: FunctionReturnType::Default(Span::default()), - return_visibility: Visibility::Private, - }; + start_span, + ); }; let generics = self.parse_generics(); - - if !self.eat_left_paren() { - // TODO: error - return FunctionDefinition { - name, - attributes, - is_unconstrained, - is_comptime, - visibility, - generics, - parameters: Vec::new(), - body: empty_body(), - span: self.span_since(start_span), - where_clause: Vec::new(), - return_type: FunctionReturnType::Default(Span::default()), - return_visibility: Visibility::Private, - }; - } - let parameters = self.parse_function_parameters(allow_self); // TODO: parse return type @@ -112,6 +86,10 @@ impl<'a> Parser<'a> { fn parse_function_parameters(&mut self, allow_self: bool) -> Vec { let mut parameters = Vec::new(); + if !self.eat_left_paren() { + return parameters; + } + if self.eat_right_paren() { return parameters; } @@ -214,6 +192,29 @@ impl<'a> Parser<'a> { } } +fn empty_fuction( + attributes: Attributes, + is_unconstrained: bool, + is_comptime: bool, + visibility: ItemVisibility, + start_span: Span, +) -> FunctionDefinition { + FunctionDefinition { + name: Ident::default(), + attributes, + is_unconstrained, + is_comptime, + visibility, + generics: Vec::new(), + parameters: Vec::new(), + body: empty_body(), + span: start_span, + where_clause: Vec::new(), + return_type: FunctionReturnType::Default(Span::default()), + return_visibility: Visibility::Private, + } +} + fn empty_body() -> BlockExpression { BlockExpression { statements: Vec::new() } } From 4ce85763e027a876a4cbae23ad283c69ef600d11 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 06:51:59 -0300 Subject: [PATCH 022/229] More patterns --- .../src/parser/parser/function.rs | 22 ++---- .../src/parser/parser/pattern.rs | 72 ++++++++++++++++++- 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 3a8195ad97f..d6d24f4cb15 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -90,24 +90,14 @@ impl<'a> Parser<'a> { return parameters; } - if self.eat_right_paren() { - return parameters; - } - loop { - let start_span = self.current_token_span; - - let pattern = self.parse_pattern(); - - // Check if the parser advanced - if self.current_token_span == start_span { - if !self.eat_right_paren() { - // TODO: error - } - + if self.eat_right_paren() { break; } + let start_span = self.current_token_span; + let pattern = self.parse_pattern(); + if self.eat_colon() { let visibility = self.parse_visibility(); @@ -129,10 +119,6 @@ impl<'a> Parser<'a> { } self.eat_commas(); - - if self.eat_right_paren() { - break; - } } parameters diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index ada2ee0c1d2..bf05c14f6c3 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -1,13 +1,57 @@ -use crate::ast::Pattern; +use crate::{ + ast::Pattern, + token::{Keyword, Token, TokenKind}, +}; use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_pattern(&mut self) -> Pattern { + let start_span = self.current_token_span; + + let mutable = self.eat_keyword(Keyword::Mut); + let pattern = self.parse_pattern_no_mut(); + if mutable { + Pattern::Mutable( + Box::new(pattern), + self.span_since(start_span), + false, // is synthesized + ) + } else { + pattern + } + } + + fn parse_pattern_no_mut(&mut self) -> Pattern { if let Some(ident) = self.eat_ident() { return Pattern::Identifier(ident); } + if let Some(token) = self.eat_kind(TokenKind::InternedPattern) { + match token.into_token() { + Token::InternedPattern(pattern) => { + return Pattern::Interned(pattern, self.previous_token_span) + } + _ => unreachable!(), + } + } + + if self.eat_left_paren() { + let start_span = self.current_token_span; + let mut patterns = Vec::new(); + loop { + if self.eat_right_paren() { + break; + } + + patterns.push(self.parse_pattern()); + + self.eat_commas(); + } + + return Pattern::Tuple(patterns, self.span_since(start_span)); + } + // TODO: parse other patterns todo!("Parser") } @@ -25,4 +69,30 @@ mod tests { let Pattern::Identifier(ident) = typ else { panic!("Expected an identifier pattern") }; assert_eq!(ident.to_string(), "foo"); } + + #[test] + fn parses_mutable_pattern() { + let src = "mut foo"; + let typ = Parser::for_str(src).parse_pattern(); + let Pattern::Mutable(pattern, _, _) = typ 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 typ = Parser::for_str(src).parse_pattern(); + let Pattern::Tuple(mut patterns, _) = typ 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"); + } } From ca320ce084ce9b09fbd4ed5750d87ecf36cbd84f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 07:24:21 -0300 Subject: [PATCH 023/229] Some Path parsing --- compiler/noirc_frontend/src/parser/parser.rs | 7 +- .../noirc_frontend/src/parser/parser/path.rs | 158 ++++++++++++++++++ .../src/parser/parser/use_tree.rs | 31 +--- 3 files changed, 162 insertions(+), 34 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/path.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 082ed5553c4..2d64d54230a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -2,7 +2,7 @@ use acvm::FieldElement; use noirc_errors::Span; use crate::{ - ast::{Ident, LValue, Path, Statement, TraitBound}, + ast::{Ident, LValue, Statement, TraitBound}, lexer::{Lexer, SpannedTokenResult}, token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; @@ -18,6 +18,7 @@ mod global; mod item; mod item_visibility; mod module; +mod path; mod pattern; mod structs; mod type_alias; @@ -105,10 +106,6 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_path_no_turbofish(&mut self) -> Path { - todo!("Parser") - } - pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { todo!("Parser") } diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs new file mode 100644 index 00000000000..5f9ae386c4c --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -0,0 +1,158 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Path, PathKind, PathSegment}, + token::{Keyword, TokenKind}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_path(&mut self) -> Path { + let (path, trailing_double_colon) = self.parse_path_impl(true); + if trailing_double_colon { + // TODO: error + } + path + } + + pub(crate) fn parse_path_no_turbofish(&mut self) -> Path { + let (path, trailing_double_colon) = self.parse_path_impl(false); + if trailing_double_colon { + // TODO: error + } + path + } + + pub(super) fn parse_path_impl(&mut self, allow_turbofish: bool) -> (Path, bool) { + let start_span = self.current_token_span; + + let kind = self.parse_path_kind(); + if kind != PathKind::Plain && !self.eat_double_colon() { + // TODO: error + } + + self.parse_path_after_kind(kind, start_span) + } + + pub(super) fn parse_path_after_kind( + &mut self, + kind: PathKind, + start_span: Span, + ) -> (Path, bool) { + let mut trailing_double_colon = false; + let mut segments = Vec::new(); + + if self.token.kind() == TokenKind::Ident { + while let Some(ident) = self.eat_ident() { + let span = ident.span(); + segments.push(PathSegment { ident, generics: None, span }); + if self.eat_double_colon() { + trailing_double_colon = true; + } else { + trailing_double_colon = false; + break; + } + } + } else { + // TODO: error + } + + let span = if segments.is_empty() && kind == PathKind::Plain { + start_span + } else { + self.span_since(start_span) + }; + + (Path { segments, kind, span }, trailing_double_colon) + } + + pub(super) fn parse_path_kind(&mut self) -> PathKind { + 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 + } + } +} + +#[cfg(test)] +mod tests { + + use crate::{ast::PathKind, parser::Parser}; + + #[test] + fn parses_plain_one_segment() { + let src = "foo"; + let path = Parser::for_str(src).parse_path(); + 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()); + } + + #[test] + fn parses_plain_two_segments() { + let src = "foo::bar"; + let path = Parser::for_str(src).parse_path(); + 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 parses_crate_two_segments() { + let src = "crate::foo::bar"; + let path = Parser::for_str(src).parse_path(); + 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()); + } + + #[test] + fn parses_super_two_segments() { + let src = "super::foo::bar"; + let path = Parser::for_str(src).parse_path(); + 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 parses_dep_two_segments() { + let src = "dep::foo::bar"; + let path = Parser::for_str(src).parse_path(); + 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(); + assert_eq!(parser.errors.len(), 0); // TODO: this should be 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()); + assert_eq!(path.span.end() as usize, src.len()); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 489bf44f47e..3d06abf8a77 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -1,7 +1,7 @@ use noirc_errors::Span; use crate::{ - ast::{Ident, Path, PathKind, PathSegment, UseTree, UseTreeKind}, + ast::{Ident, Path, PathKind, UseTree, UseTreeKind}, token::Keyword, }; @@ -28,22 +28,7 @@ impl<'a> Parser<'a> { start_span: Span, kind: PathKind, ) -> UseTree { - let mut segments = Vec::new(); - let mut trailing_double_colon = false; - - while let Some(ident) = self.eat_ident() { - let span = ident.span(); - segments.push(PathSegment { ident, generics: None, span }); - if self.eat_double_colon() { - trailing_double_colon = true; - } else { - trailing_double_colon = false; - break; - } - } - - let span = if segments.is_empty() { start_span } else { self.span_since(start_span) }; - let prefix = Path { segments, kind, span }; + let (prefix, mut trailing_double_colon) = self.parse_path_after_kind(kind, start_span); if prefix.segments.is_empty() && kind != PathKind::Plain { trailing_double_colon = true; @@ -99,18 +84,6 @@ impl<'a> Parser<'a> { } } } - - pub(super) fn parse_path_kind(&mut self) -> PathKind { - 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 - } - } } #[cfg(test)] From fbd24512e4a0d79d5ecf154266d8770ba6fab1ef Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 09:24:53 -0300 Subject: [PATCH 024/229] Start parsing struct patterns --- compiler/noirc_frontend/src/ast/statement.rs | 8 +- .../src/parser/parser/pattern.rs | 104 ++++++++++++++---- 2 files changed, 90 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 034739f16f6..23b3070f960 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -462,7 +462,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> { @@ -479,6 +481,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(); diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index bf05c14f6c3..15f3842e22e 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -1,5 +1,5 @@ use crate::{ - ast::Pattern, + ast::{Ident, Path, Pattern}, token::{Keyword, Token, TokenKind}, }; @@ -23,37 +23,80 @@ impl<'a> Parser<'a> { } fn parse_pattern_no_mut(&mut self) -> Pattern { - if let Some(ident) = self.eat_ident() { + if let Some(pattern) = self.parse_interned_pattern() { + return pattern; + } + + if let Some(pattern) = self.parse_tuple_pattern() { + return pattern; + } + + let mut path = self.parse_path(); + if path.is_empty() { + // TODO: error + return Pattern::Identifier(Ident::default()); + } + + if self.eat_left_brace() { + return self.parse_struct_pattern(path); + } + + if !path.is_ident() { + // TODO: error + let ident = path.segments.pop().unwrap().ident; return Pattern::Identifier(ident); } - if let Some(token) = self.eat_kind(TokenKind::InternedPattern) { - match token.into_token() { - Token::InternedPattern(pattern) => { - return Pattern::Interned(pattern, self.previous_token_span) - } - _ => unreachable!(), + let ident = path.segments.remove(0).ident; + Pattern::Identifier(ident) + } + + fn parse_tuple_pattern(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.eat_left_paren() { + return None; + } + + let mut patterns = Vec::new(); + loop { + if self.eat_right_paren() { + break; } + + patterns.push(self.parse_pattern()); + + self.eat_commas(); } - if self.eat_left_paren() { - let start_span = self.current_token_span; - let mut patterns = Vec::new(); - loop { - if self.eat_right_paren() { - break; - } + Some(Pattern::Tuple(patterns, self.span_since(start_span))) + } - patterns.push(self.parse_pattern()); + fn parse_struct_pattern(&mut self, path: Path) -> Pattern { + let start_span = path.span(); - self.eat_commas(); - } + let mut patterns = Vec::new(); - return Pattern::Tuple(patterns, self.span_since(start_span)); + loop { + if self.eat_right_brace() { + break; + } } - // TODO: parse other patterns - todo!("Parser") + Pattern::Struct(path, patterns, self.span_since(start_span)) + } + + 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!(), + } } } @@ -95,4 +138,23 @@ mod tests { let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; assert_eq!(ident.to_string(), "bar"); } + + #[test] + fn parses_struct_pattern_no_fields() { + let src = "foo::Bar {}"; + let typ = Parser::for_str(src).parse_pattern(); + let Pattern::Struct(path, patterns, _) = typ 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 typ = Parser::for_str(src).parse_pattern(); + let Pattern::Struct(path, mut patterns, _) = typ else { + panic!("Expected a struct pattern") + }; + assert_eq!(path.to_string(), "foo::Bar"); + } } From 8f5dd2fe8d56dce33ee6c82b647472b8f93ca525 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 09:56:23 -0300 Subject: [PATCH 025/229] Finish parsing patterns, and add some TODOs --- .../src/parser/parser/function.rs | 1 + .../src/parser/parser/generics.rs | 2 ++ .../src/parser/parser/pattern.rs | 24 +++++++++++++++++++ .../src/parser/parser/structs.rs | 1 + .../src/parser/parser/use_tree.rs | 1 + 5 files changed, 29 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index d6d24f4cb15..6223383a76d 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -119,6 +119,7 @@ impl<'a> Parser<'a> { } self.eat_commas(); + // TODO: error if no commas between parameters } parameters diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 00a1cf2249b..0e6eeabd5be 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -22,6 +22,8 @@ impl<'a> Parser<'a> { generics.push(generic); self.eat_commas(); + // TODO: error if no commas between generics + if self.eat_greater() { break; } diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 15f3842e22e..c4965d50452 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -67,6 +67,7 @@ impl<'a> Parser<'a> { patterns.push(self.parse_pattern()); self.eat_commas(); + // TODO: error if no commas between patterns } Some(Pattern::Tuple(patterns, self.span_since(start_span))) @@ -81,6 +82,20 @@ impl<'a> Parser<'a> { if self.eat_right_brace() { break; } + + let Some(ident) = self.eat_ident() else { + // TODO: error + break; + }; + + if self.eat_colon() { + patterns.push((ident, self.parse_pattern())); + } else { + patterns.push((ident.clone(), Pattern::Identifier(ident))); + } + + self.eat_commas(); + // TODO: error if no comma between patterns } Pattern::Struct(path, patterns, self.span_since(start_span)) @@ -156,5 +171,14 @@ mod tests { 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"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 43950d83eb4..e4ca4abdf98 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -53,6 +53,7 @@ impl<'a> Parser<'a> { fields.push(Documented::new(StructField { name, typ }, doc_comments)); self.eat_commas(); + // TODO: error if no comma between fields } if !self.eat_right_brace() { diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 3d06abf8a77..943eacd6b15 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -51,6 +51,7 @@ impl<'a> Parser<'a> { use_trees.push(use_tree); self.eat_commas(); + // TODO: error if no comma between use trees if self.eat_right_brace() { break; From 74a60e2957a425e8aaca5a3f2cba73ed14e57f49 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 09:59:59 -0300 Subject: [PATCH 026/229] typo --- compiler/noirc_frontend/src/parser/parser/function.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 6223383a76d..7459b1b5ec8 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -45,7 +45,7 @@ impl<'a> Parser<'a> { let Some(name) = self.eat_ident() else { // TODO: error - return empty_fuction( + return empty_function( attributes, is_unconstrained, is_comptime, @@ -179,7 +179,7 @@ impl<'a> Parser<'a> { } } -fn empty_fuction( +fn empty_function( attributes: Attributes, is_unconstrained: bool, is_comptime: bool, From 9acd5b3254fa8078f8750708cae40a1b3af3a62e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 10:02:17 -0300 Subject: [PATCH 027/229] Parse function return type --- .../src/parser/parser/function.rs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 7459b1b5ec8..e920a21fe3e 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -57,7 +57,11 @@ impl<'a> Parser<'a> { let generics = self.parse_generics(); let parameters = self.parse_function_parameters(allow_self); - // TODO: parse return type + let return_type = if self.eat(Token::Arrow) { + FunctionReturnType::Ty(self.parse_type()) + } else { + FunctionReturnType::Default(Span::default()) + }; // TODO: parse where clause @@ -78,7 +82,7 @@ impl<'a> Parser<'a> { body, span: start_span, where_clause: Vec::new(), - return_type: FunctionReturnType::Default(Span::default()), + return_type, return_visibility: Visibility::Private, } } @@ -209,7 +213,7 @@ fn empty_body() -> BlockExpression { #[cfg(test)] mod tests { use crate::{ - ast::Visibility, + ast::{UnresolvedTypeData, Visibility}, parser::{parser::parse_program, ItemKind}, }; @@ -313,4 +317,17 @@ mod tests { 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 (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Function(noir_function) = &item.kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); + } } From 0da3c3e64d34cf9ab56298f3d39206d7adf2274c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 10:04:16 -0300 Subject: [PATCH 028/229] Parse function return visibility --- .../src/parser/parser/function.rs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index e920a21fe3e..c47d60b0269 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -57,10 +57,11 @@ impl<'a> Parser<'a> { let generics = self.parse_generics(); let parameters = self.parse_function_parameters(allow_self); - let return_type = if self.eat(Token::Arrow) { - FunctionReturnType::Ty(self.parse_type()) + let (return_type, return_visibility) = if self.eat(Token::Arrow) { + let visibility = self.parse_visibility(); + (FunctionReturnType::Ty(self.parse_type()), visibility) } else { - FunctionReturnType::Default(Span::default()) + (FunctionReturnType::Default(Span::default()), Visibility::Private) }; // TODO: parse where clause @@ -83,7 +84,7 @@ impl<'a> Parser<'a> { span: start_span, where_clause: Vec::new(), return_type, - return_visibility: Visibility::Private, + return_visibility, } } @@ -328,6 +329,21 @@ mod tests { let ItemKind::Function(noir_function) = &item.kind else { panic!("Expected function"); }; + assert_eq!(noir_function.def.return_visibility, Visibility::Private); + assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); + } + + #[test] + fn parse_function_return_visibility() { + let src = "fn foo() -> pub Field {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Function(noir_function) = &item.kind else { + panic!("Expected function"); + }; + assert_eq!(noir_function.def.return_visibility, Visibility::Public); assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); } } From ca00a2f3923ef973c6dd4a1ecee39e37565e5bc0 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 10:11:23 -0300 Subject: [PATCH 029/229] TODO for parsing where clause --- compiler/noirc_frontend/src/parser/parser.rs | 1 + compiler/noirc_frontend/src/parser/parser/function.rs | 6 +++--- .../noirc_frontend/src/parser/parser/where_clause.rs | 10 ++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/where_clause.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 2d64d54230a..c1c1963f40c 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -24,6 +24,7 @@ mod structs; mod type_alias; mod types; mod use_tree; +mod where_clause; /// Entry function for the parser - also handles lexing internally. /// diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index c47d60b0269..eb46efa199b 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -9,7 +9,7 @@ use crate::{ token::{Attribute, Attributes, Keyword, Token}, }; -use super::Parser; +use super::{where_clause, Parser}; impl<'a> Parser<'a> { pub(crate) fn parse_function( @@ -64,7 +64,7 @@ impl<'a> Parser<'a> { (FunctionReturnType::Default(Span::default()), Visibility::Private) }; - // TODO: parse where clause + let where_clause = self.parse_where_clause(); let body = if let Token::LeftBrace = self.token.token() { self.parse_block_expression() @@ -82,7 +82,7 @@ impl<'a> Parser<'a> { parameters, body, span: start_span, - where_clause: Vec::new(), + where_clause, return_type, return_visibility, } 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..5324482734f --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -0,0 +1,10 @@ +use crate::ast::UnresolvedTraitConstraint; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(super) fn parse_where_clause(&mut self) -> Vec { + // TODO: parse where clause + Vec::new() + } +} From d72f35da2b6928b915e1e2fc75c0c2d915c60585 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 10:11:31 -0300 Subject: [PATCH 030/229] Refactor type parsing --- .../noirc_frontend/src/parser/parser/types.rs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index eb69d3fdd04..7f0b6f079d9 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -11,23 +11,34 @@ impl<'a> Parser<'a> { pub(crate) fn parse_type(&mut self) -> UnresolvedType { let start_span = self.current_token_span; - // TODO: parse more types + let typ = self.parse_unresolved_type_data(); + let span = if self.current_token_span == start_span { + start_span + } else { + self.span_since(start_span) + }; + + UnresolvedType { typ, span } + } + + fn parse_unresolved_type_data(&mut self) -> UnresolvedTypeData { + if self.eat_keyword(Keyword::Field) { + return UnresolvedTypeData::FieldElement; + } - let typ = if self.eat_keyword(Keyword::Field) { - UnresolvedTypeData::FieldElement - } else if let Some(int_type) = self.eat_int_type() { - match UnresolvedTypeData::from_int_token(int_type) { + if let Some(int_type) = self.eat_int_type() { + return match UnresolvedTypeData::from_int_token(int_type) { Ok(typ) => typ, Err(_) => { // TODO: error UnresolvedTypeData::Error } - } - } else { - return UnresolvedType { typ: UnresolvedTypeData::Error, span: start_span }; - }; + }; + } + + // TODO: parse more types - UnresolvedType { typ, span: self.span_since(start_span) } + UnresolvedTypeData::Error } pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { From 5e3c894980bbc0d8ac14038b3e1aed3fe5d21df6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 10:12:58 -0300 Subject: [PATCH 031/229] Adjustments --- compiler/noirc_frontend/src/parser/parser/function.rs | 2 +- compiler/noirc_frontend/src/parser/parser/types.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index eb46efa199b..fd425842c53 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -9,7 +9,7 @@ use crate::{ token::{Attribute, Attributes, Keyword, Token}, }; -use super::{where_clause, Parser}; +use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_function( diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 7f0b6f079d9..b82abf67739 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -52,7 +52,6 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, parser::Parser, From 4671572ac8b65288d2309af7e157adc6442b9ce2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 10:32:14 -0300 Subject: [PATCH 032/229] Some type parsing --- .../noirc_frontend/src/parser/parser/types.rs | 121 +++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index b82abf67739..0ebce344bf5 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,8 +1,8 @@ use noirc_errors::Span; use crate::{ - ast::{UnresolvedType, UnresolvedTypeData}, - token::Keyword, + ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData}, + token::{Keyword, Token}, }; use super::Parser; @@ -22,6 +22,14 @@ impl<'a> Parser<'a> { } fn parse_unresolved_type_data(&mut self) -> UnresolvedTypeData { + if self.eat_left_paren() { + return self.parse_parentheses_type(); + } + + if self.eat_keyword(Keyword::Bool) { + return UnresolvedTypeData::Bool; + } + if self.eat_keyword(Keyword::Field) { return UnresolvedTypeData::FieldElement; } @@ -36,11 +44,50 @@ impl<'a> Parser<'a> { }; } + if self.eat(Token::Ampersand) { + if !self.eat_keyword(Keyword::Mut) { + // TODO: error + } + return UnresolvedTypeData::MutableReference(Box::new(self.parse_type())); + }; + + let path = self.parse_path_no_turbofish(); + if !path.is_empty() { + // TODO: parse generics + let generics = GenericTypeArgs::default(); + return UnresolvedTypeData::Named(path, generics, false); + } + // TODO: parse more types UnresolvedTypeData::Error } + fn parse_parentheses_type(&mut self) -> UnresolvedTypeData { + if self.eat_right_paren() { + return UnresolvedTypeData::Unit; + } + + let mut types = Vec::new(); + let mut trailing_comma; + loop { + types.push(self.parse_type()); + + trailing_comma = self.eat_commas(); + // TODO: error if no comma between types + + if self.eat_right_paren() { + break; + } + } + + if types.len() == 1 && !trailing_comma { + UnresolvedTypeData::Parenthesized(Box::new(types.remove(0))) + } else { + UnresolvedTypeData::Tuple(types) + } + } + pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { if self.eat_colon() { self.parse_type() @@ -57,6 +104,20 @@ mod tests { parser::Parser, }; + #[test] + fn parses_unit_type() { + let src = "()"; + let typ = Parser::for_str(src).parse_type(); + assert!(matches!(typ.typ, UnresolvedTypeData::Unit)); + } + + #[test] + fn parses_bool_type() { + let src = "bool"; + let typ = Parser::for_str(src).parse_type(); + assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); + } + #[test] fn parses_int_type() { let src = "u32"; @@ -73,4 +134,60 @@ mod tests { let typ = Parser::for_str(src).parse_type(); assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } + + #[test] + fn parses_tuple_type() { + let src = "(Field, bool)"; + let typ = Parser::for_str(src).parse_type(); + 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 = Parser::for_str(src).parse_type(); + 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 = Parser::for_str(src).parse_type(); + 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 = Parser::for_str(src).parse_type(); + 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 = Parser::for_str(src).parse_type(); + let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { + panic!("Expected a named type") + }; + assert_eq!(path.to_string(), "foo::Bar"); + assert!(generics.is_empty()); + } } From 41ed799f0ef2a876e7826288465522d67aeb1e61 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 11:43:30 -0300 Subject: [PATCH 033/229] Parse some more expressions --- .../src/parser/parser/expression.rs | 155 ++++++++++++++++-- .../src/parser/parser/function.rs | 6 +- .../noirc_frontend/src/parser/parser/types.rs | 16 +- 3 files changed, 150 insertions(+), 27 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 43e7cb02fa8..b8118fcbba6 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,37 +1,94 @@ -use noirc_errors::Span; - -use crate::ast::{BlockExpression, Expression, ExpressionKind}; +use crate::ast::{BlockExpression, Expression, ExpressionKind, Literal, Statement, StatementKind}; use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_expression(&mut self) -> Expression { - // TODO: parse other expressions - let start_span = self.current_token_span; - - let kind = if let Some(int) = self.eat_int() { - ExpressionKind::integer(int) + let kind = self.parse_expression_kind(); + let span = if start_span == self.current_token_span { + start_span } else { - return Expression { kind: ExpressionKind::Error, span: Span::default() }; + self.span_since(start_span) }; - Expression { kind, span: self.span_since(start_span) } + Expression { kind, span } + } + + fn parse_expression_kind(&mut self) -> ExpressionKind { + if let Some(int) = self.eat_int() { + return ExpressionKind::integer(int); + } + + if let Some(kind) = self.parse_parentheses_expression() { + return kind; + } + + if let Some(kind) = self.parse_block_expression() { + return ExpressionKind::Block(kind); + } + + // TODO: parse other expressions + + ExpressionKind::Error + } + + 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 = Vec::new(); + let mut trailing_comma; + loop { + exprs.push(self.parse_expression()); + + trailing_comma = self.eat_commas(); + // TODO: error if no comma between exprs + + if self.eat_right_paren() { + break; + } + } + + Some(if exprs.len() == 1 && !trailing_comma { + ExpressionKind::Parenthesized(Box::new(exprs.remove(0))) + } else { + ExpressionKind::Tuple(exprs) + }) } - pub(super) fn parse_block_expression(&mut self) -> BlockExpression { - self.eat_left_brace(); - // TODO: parse statements - self.eat_right_brace(); + pub(super) fn parse_block_expression(&mut self) -> Option { + if !self.eat_left_brace() { + return None; + } + + let mut statements = Vec::new(); - BlockExpression { statements: Vec::new() } + if self.eat_right_brace() { + return Some(BlockExpression { statements }); + } + + let expr = self.parse_expression(); + let span = expr.span; + statements.push(Statement { kind: StatementKind::Expression(expr), span }); + + if !self.eat_right_brace() { + // TODO: error + } + + Some(BlockExpression { statements }) } } #[cfg(test)] mod tests { use crate::{ - ast::{ExpressionKind, Literal}, + ast::{ExpressionKind, Literal, StatementKind}, parser::Parser, }; @@ -45,4 +102,70 @@ mod tests { assert_eq!(field, 42_u128.into()); assert!(!negative); } + + #[test] + fn parses_parenthesized_expression() { + let src = "(42)"; + let expr = Parser::for_str(src).parse_expression(); + 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 = Parser::for_str(src).parse_expression(); + assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Unit))); + } + + #[test] + fn parses_tuple_expression() { + let src = "(1, 2)"; + let expr = Parser::for_str(src).parse_expression(); + 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 = Parser::for_str(src).parse_expression(); + 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); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index fd425842c53..cf862cffa79 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -66,11 +66,7 @@ impl<'a> Parser<'a> { let where_clause = self.parse_where_clause(); - let body = if let Token::LeftBrace = self.token.token() { - self.parse_block_expression() - } else { - empty_body() - }; + let body = self.parse_block_expression().unwrap_or_else(|| empty_body()); FunctionDefinition { name, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 0ebce344bf5..821053bb97b 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -22,8 +22,8 @@ impl<'a> Parser<'a> { } fn parse_unresolved_type_data(&mut self) -> UnresolvedTypeData { - if self.eat_left_paren() { - return self.parse_parentheses_type(); + if let Some(typ) = self.parse_parentheses_type() { + return typ; } if self.eat_keyword(Keyword::Bool) { @@ -63,9 +63,13 @@ impl<'a> Parser<'a> { UnresolvedTypeData::Error } - fn parse_parentheses_type(&mut self) -> UnresolvedTypeData { + fn parse_parentheses_type(&mut self) -> Option { + if !self.eat_left_paren() { + return None; + } + if self.eat_right_paren() { - return UnresolvedTypeData::Unit; + return Some(UnresolvedTypeData::Unit); } let mut types = Vec::new(); @@ -81,11 +85,11 @@ impl<'a> Parser<'a> { } } - if types.len() == 1 && !trailing_comma { + Some(if types.len() == 1 && !trailing_comma { UnresolvedTypeData::Parenthesized(Box::new(types.remove(0))) } else { UnresolvedTypeData::Tuple(types) - } + }) } pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { From 19ea2c62eeca619802743d347d77f50c55dfc9f9 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 11:58:08 -0300 Subject: [PATCH 034/229] Fix FunctionDefinition::to_string() duplicating `fn` keyword --- compiler/noirc_frontend/src/ast/expression.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index c581ea9d62a..91eab68cc7d 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -863,7 +863,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) } } From 5754dca7f893d414aee2a305315ccb079df65d7c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 12:39:34 -0300 Subject: [PATCH 035/229] Parse bool literals --- compiler/noirc_frontend/src/parser/parser.rs | 16 ++++++++++++++-- .../src/parser/parser/expression.rs | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index c1c1963f40c..e855857483e 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -190,8 +190,7 @@ impl<'a> Parser<'a> { } fn eat_int(&mut self) -> Option { - let is_int = matches!(self.token.token(), Token::Int(..)); - if is_int { + if matches!(self.token.token(), Token::Int(..)) { let token = std::mem::take(&mut self.token); self.next_token(); match token.into_token() { @@ -203,6 +202,19 @@ impl<'a> Parser<'a> { } } + fn eat_bool(&mut self) -> Option { + if matches!(self.token.token(), Token::Bool(..)) { + let token = std::mem::take(&mut self.token); + self.next_token(); + match token.into_token() { + Token::Bool(bool) => Some(bool), + _ => unreachable!(), + } + } else { + None + } + } + fn eat_comma(&mut self) -> bool { self.eat(Token::Comma) } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index b8118fcbba6..302b8fa2c9b 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -16,6 +16,10 @@ impl<'a> Parser<'a> { } fn parse_expression_kind(&mut self) -> ExpressionKind { + if let Some(bool) = self.eat_bool() { + return ExpressionKind::Literal(Literal::Bool(bool)); + } + if let Some(int) = self.eat_int() { return ExpressionKind::integer(int); } @@ -92,6 +96,17 @@ mod tests { parser::Parser, }; + #[test] + fn parses_bool_literals() { + let src = "true"; + let expr = Parser::for_str(src).parse_expression(); + assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(true)))); + + let src = "false"; + let expr = Parser::for_str(src).parse_expression(); + assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(false)))); + } + #[test] fn parses_integer_literal() { let src = "42"; From f38f2c2111ca99bb50d1c44453996ba63eb6c1af Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 12:49:39 -0300 Subject: [PATCH 036/229] Parse empty impls --- compiler/noirc_frontend/src/parser/parser.rs | 9 ++- .../src/parser/parser/expression.rs | 6 +- .../noirc_frontend/src/parser/parser/impls.rs | 61 +++++++++++++++++++ .../noirc_frontend/src/parser/parser/item.rs | 5 ++ .../noirc_frontend/src/parser/parser/path.rs | 6 +- .../noirc_frontend/src/parser/parser/types.rs | 6 +- 6 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/impls.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index e855857483e..860372161d3 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -15,6 +15,7 @@ mod expression; mod function; mod generics; mod global; +mod impls; mod item; mod item_visibility; mod module; @@ -291,7 +292,11 @@ impl<'a> Parser<'a> { } fn span_since(&self, start_span: Span) -> Span { - let end_span = self.previous_token_span; - Span::from(start_span.start()..end_span.end()) + 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()) + } } } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 302b8fa2c9b..7b4d583dbcb 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -6,11 +6,7 @@ impl<'a> Parser<'a> { pub(crate) fn parse_expression(&mut self) -> Expression { let start_span = self.current_token_span; let kind = self.parse_expression_kind(); - let span = if start_span == self.current_token_span { - start_span - } else { - self.span_since(start_span) - }; + let span = self.span_since(start_span); Expression { kind, span } } 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..6d279e6981c --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -0,0 +1,61 @@ +use noirc_errors::Span; + +use crate::ast::TypeImpl; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_impl(&mut self, start_span: Span) -> TypeImpl { + let generics = self.parse_generics(); + + let type_span_start = self.current_token_span; + let object_type = self.parse_type(); + let type_span = self.span_since(type_span_start); + + let where_clause = self.parse_where_clause(); + + // TODO: methods + let methods = Vec::new(); + + if self.eat_left_brace() { + self.eat_right_brace(); + } + + TypeImpl { object_type, type_span, generics, where_clause, methods } + } +} + +#[cfg(test)] +mod tests { + use crate::parser::{parser::parse_program, ItemKind}; + + #[test] + fn parse_empty_impl() { + let src = "impl Foo {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + 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!(type_impl.generics.is_empty()); + assert!(type_impl.methods.is_empty()); + } + + #[test] + fn parse_empty_impl_with_generics() { + let src = "impl Foo {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + 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.generics.len(), 2); + assert!(type_impl.methods.is_empty()); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index c7a6a22726a..9922cceedf7 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -62,6 +62,11 @@ impl<'a> Parser<'a> { return Some(ItemKind::Struct(self.parse_struct(attributes, visibility, start_span))); } + if self.eat_keyword(Keyword::Impl) { + // TODO: error if there's comptime or mutable or unconstrained + return Some(ItemKind::Impl(self.parse_impl(start_span))); + } + if self.eat_keyword(Keyword::Global) { // TODO: error if there's unconstrained return Some(ItemKind::Global(self.parse_global(attributes, comptime, mutable))); diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 5f9ae386c4c..9a9856e8621 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -58,11 +58,7 @@ impl<'a> Parser<'a> { // TODO: error } - let span = if segments.is_empty() && kind == PathKind::Plain { - start_span - } else { - self.span_since(start_span) - }; + let span = self.span_since(start_span); (Path { segments, kind, span }, trailing_double_colon) } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 821053bb97b..a4b0f484ac1 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -12,11 +12,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let typ = self.parse_unresolved_type_data(); - let span = if self.current_token_span == start_span { - start_span - } else { - self.span_since(start_span) - }; + let span = self.span_since(start_span); UnresolvedType { typ, span } } From 7089c35fd95c23d943ac081b2985c836fbcc4853 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 13:06:19 -0300 Subject: [PATCH 037/229] Parse impls --- compiler/noirc_frontend/src/parser/parser.rs | 4 + .../noirc_frontend/src/parser/parser/impls.rs | 115 ++++++++++++++++-- .../noirc_frontend/src/parser/parser/item.rs | 2 +- 3 files changed, 112 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 860372161d3..90af18cb140 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -282,6 +282,10 @@ impl<'a> Parser<'a> { } } + fn is_eof(&mut self) -> bool { + self.token.token() == &Token::EOF + } + fn eat(&mut self, token: Token) -> bool { if self.token.token() == &token { self.next_token(); diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 6d279e6981c..1f89c39f5e1 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -1,11 +1,14 @@ use noirc_errors::Span; -use crate::ast::TypeImpl; +use crate::{ + ast::{Documented, NoirFunction, TypeImpl}, + token::Keyword, +}; use super::Parser; impl<'a> Parser<'a> { - pub(crate) fn parse_impl(&mut self, start_span: Span) -> TypeImpl { + pub(crate) fn parse_impl(&mut self) -> TypeImpl { let generics = self.parse_generics(); let type_span_start = self.current_token_span; @@ -13,21 +16,64 @@ impl<'a> Parser<'a> { let type_span = self.span_since(type_span_start); let where_clause = self.parse_where_clause(); + let methods = self.parse_impl_body(); - // TODO: methods - let methods = Vec::new(); + TypeImpl { object_type, type_span, generics, where_clause, methods } + } + + fn parse_impl_body(&mut self) -> Vec<(Documented, Span)> { + let mut methods = Vec::new(); - if self.eat_left_brace() { - self.eat_right_brace(); + if !self.eat_left_brace() { + // TODO: error + return methods; } - TypeImpl { object_type, type_span, generics, where_clause, methods } + loop { + // TODO: maybe require visibility to always come first + let doc_comments = self.parse_outer_doc_comments(); + let start_span = self.current_token_span; + let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); + let visibility = self.parse_item_visibility(); + let is_comptime = self.eat_keyword(Keyword::Comptime); + let attributes = Vec::new(); + let allow_self = true; + + if self.eat_keyword(Keyword::Fn) { + let method = self.parse_function( + attributes, + visibility, + is_comptime, + is_unconstrained, + allow_self, + start_span, + ); + methods.push((Documented::new(method, doc_comments), self.span_since(start_span))); + + if self.eat_right_brace() { + break; + } + } else { + // TODO: error if visibility, unconstrained or comptime were found + + if !self.eat_right_brace() { + // TODO: error + } + + break; + } + } + + methods } } #[cfg(test)] mod tests { - use crate::parser::{parser::parse_program, ItemKind}; + use crate::{ + ast::ItemVisibility, + parser::{parser::parse_program, ItemKind}, + }; #[test] fn parse_empty_impl() { @@ -58,4 +104,57 @@ mod tests { 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Impl(mut type_impl) = item.kind else { + panic!("Expected type impl"); + }; + 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_empty_impl_missing_right_brace() { + let src = "impl Foo {"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); // TODO: there should be an error here + 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"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); // TODO: there should be errors here + 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"); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 9922cceedf7..ae2d7575a68 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -64,7 +64,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Impl) { // TODO: error if there's comptime or mutable or unconstrained - return Some(ItemKind::Impl(self.parse_impl(start_span))); + return Some(ItemKind::Impl(self.parse_impl())); } if self.eat_keyword(Keyword::Global) { From 47d7b52faf83603c7067368b1c9d42036627d7dc Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 13:19:18 -0300 Subject: [PATCH 038/229] Avoid looping forever --- compiler/noirc_frontend/src/parser/parser.rs | 4 --- .../src/parser/parser/expression.rs | 20 +++++++++++-- .../src/parser/parser/function.rs | 18 ++++++++++++ .../src/parser/parser/pattern.rs | 29 ++++++++++++++++++- .../src/parser/parser/structs.rs | 13 +++++++++ .../noirc_frontend/src/parser/parser/types.rs | 24 +++++++++++++-- 6 files changed, 99 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 90af18cb140..860372161d3 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -282,10 +282,6 @@ impl<'a> Parser<'a> { } } - fn is_eof(&mut self) -> bool { - self.token.token() == &Token::EOF - } - fn eat(&mut self, token: Token) -> bool { if self.token.token() == &token { self.next_token(); diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 7b4d583dbcb..95edc1a7767 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -43,9 +43,18 @@ impl<'a> Parser<'a> { } let mut exprs = Vec::new(); - let mut trailing_comma; + let mut trailing_comma = false; loop { - exprs.push(self.parse_expression()); + let expr = self.parse_expression(); + if let ExpressionKind::Error = expr.kind { + // TODO: error + self.eat_right_paren(); + if exprs.is_empty() { + return Some(ExpressionKind::Error); + } + break; + } + exprs.push(expr); trailing_comma = self.eat_commas(); // TODO: error if no comma between exprs @@ -159,6 +168,13 @@ mod tests { assert!(!negative); } + #[test] + fn parses_unclosed_parentheses() { + let src = "("; + let expr = Parser::for_str(src).parse_expression(); + assert!(matches!(expr.kind, ExpressionKind::Error)); + } + #[test] fn parses_block_expression_with_a_single_expression() { let src = "{ 1 }"; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index cf862cffa79..193c432c7a4 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -98,6 +98,11 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let pattern = self.parse_pattern(); + if self.current_token_span == start_span { + // TODO: error + self.eat_right_paren(); + break; + } if self.eat_colon() { let visibility = self.parse_visibility(); @@ -342,4 +347,17 @@ mod tests { assert_eq!(noir_function.def.return_visibility, Visibility::Public); assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); } + + #[test] + fn parse_function_unclosed_parentheses() { + let src = "fn foo(x: i32,"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); // TODO: there should be errors here + 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()); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index c4965d50452..192fff7d37b 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -64,7 +64,15 @@ impl<'a> Parser<'a> { break; } - patterns.push(self.parse_pattern()); + let start_span = self.current_token_span; + let pattern = self.parse_pattern(); + if self.current_token_span == start_span { + // TODO: error + self.eat_right_paren(); + break; + } + + patterns.push(pattern); self.eat_commas(); // TODO: error if no commas between patterns @@ -154,6 +162,16 @@ mod tests { assert_eq!(ident.to_string(), "bar"); } + #[test] + fn parses_unclosed_tuple_pattern() { + let src = "(foo,"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_pattern(); + assert!(parser.errors.is_empty()); // TODO: there should be an error here + let Pattern::Tuple(patterns, _) = typ else { panic!("Expected a tuple pattern") }; + assert_eq!(patterns.len(), 1); + } + #[test] fn parses_struct_pattern_no_fields() { let src = "foo::Bar {}"; @@ -181,4 +199,13 @@ mod tests { 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 typ = parser.parse_pattern(); + let Pattern::Struct(path, _, _) = typ else { panic!("Expected a struct pattern") }; + assert_eq!(path.to_string(), "foo::Bar"); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index e4ca4abdf98..ae411ec31b9 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -180,4 +180,17 @@ mod tests { }; assert_eq!("Foo", noir_struct.name.to_string()); } + + #[test] + fn parse_unclosed_struct() { + let src = "struct Foo {"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); // TODO: there should be an error here + 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()); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index a4b0f484ac1..dd5e9cc6023 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -69,9 +69,17 @@ impl<'a> Parser<'a> { } let mut types = Vec::new(); - let mut trailing_comma; + let mut trailing_comma = false; loop { - types.push(self.parse_type()); + let start_span = self.current_token_span; + let typ = self.parse_type(); + if self.current_token_span == start_span { + // TODO: error + self.eat_right_paren(); + break; + } + + types.push(typ); trailing_comma = self.eat_commas(); // TODO: error if no comma between types @@ -170,6 +178,18 @@ mod tests { assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } + #[test] + fn parses_unclosed_parentheses_type() { + let src = "(Field"; + let mut parser = Parser::for_str(src); + assert!(parser.errors.is_empty()); // TODO: there should be an error here + let typ = parser.parse_type(); + 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"; From e9510f47754312df1d9b9befa6eb6aa9f4867ecf Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 13:56:20 -0300 Subject: [PATCH 039/229] Start producing and testing errors --- .../src/parser/parser/attributes.rs | 30 +++++++++++-------- .../src/parser/parser/function.rs | 26 ++++++++++++---- .../src/parser/parser/global.rs | 2 +- .../src/parser/parser/module.rs | 4 ++- .../src/parser/parser/structs.rs | 14 +++++++-- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 81b0a8ba730..0409d1ef7db 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,3 +1,6 @@ +use noirc_errors::Span; + +use crate::parser::{ParserError, ParserErrorReason}; use crate::token::SecondaryAttribute; use crate::token::{Attribute, Token, TokenKind}; @@ -12,13 +15,13 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_attributes(&mut self) -> Vec { - let mut attributes: Vec = Vec::new(); + pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Span)> { + let mut attributes = Vec::new(); while let Some(token) = self.eat_kind(TokenKind::Attribute) { match token.into_token() { Token::Attribute(attribute) => { - attributes.push(attribute.clone()); + attributes.push((attribute, self.previous_token_span)); } _ => unreachable!(), } @@ -29,18 +32,19 @@ impl<'a> Parser<'a> { pub(super) fn validate_secondary_attributes( &mut self, - attributes: Vec, + attributes: Vec<(Attribute, Span)>, ) -> Vec { attributes .into_iter() - .filter_map(|attribute| { - match attribute { - Attribute::Function(..) => { - // TODO: error - None - } - Attribute::Secondary(attr) => Some(attr), + .filter_map(|(attribute, span)| match attribute { + Attribute::Function(..) => { + self.errors.push(ParserError::with_reason( + ParserErrorReason::NoFunctionAttributesAllowedOnStruct, + span, + )); + None } + Attribute::Secondary(attr) => Some(attr), }) .collect() } @@ -69,10 +73,10 @@ mod tests { let mut attributes = Parser::for_str(src).parse_attributes(); assert_eq!(attributes.len(), 2); - let attr = attributes.remove(0); + let (attr, _) = attributes.remove(0); assert!(matches!(attr, Attribute::Function(FunctionAttribute::Test(TestScope::None)))); - let attr = attributes.remove(0); + let (attr, _) = attributes.remove(0); assert!(matches!(attr, Attribute::Secondary(SecondaryAttribute::Deprecated(None)))); } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 193c432c7a4..4407442405b 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -6,6 +6,7 @@ use crate::{ BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NoirFunction, Param, UnresolvedType, UnresolvedTypeData, Visibility, }, + parser::{ParserError, ParserErrorReason}, token::{Attribute, Attributes, Keyword, Token}, }; @@ -14,7 +15,7 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_function( &mut self, - attributes: Vec, + attributes: Vec<(Attribute, Span)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -34,7 +35,7 @@ impl<'a> Parser<'a> { pub(crate) fn parse_function_definition( &mut self, - attributes: Vec, + attributes: Vec<(Attribute, Span)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -165,15 +166,18 @@ impl<'a> Parser<'a> { Visibility::Private } - fn validate_attributes(&mut self, attributes: Vec) -> Attributes { + fn validate_attributes(&mut self, attributes: Vec<(Attribute, Span)>) -> Attributes { let mut primary = None; let mut secondary = Vec::new(); - for attribute in attributes { + for (attribute, span) in attributes { match attribute { Attribute::Function(attr) => { if primary.is_some() { - // TODO: err + self.errors.push(ParserError::with_reason( + ParserErrorReason::MultipleFunctionAttributesFound, + span, + )); } primary = Some(attr); } @@ -216,7 +220,7 @@ fn empty_body() -> BlockExpression { mod tests { use crate::{ ast::{UnresolvedTypeData, Visibility}, - parser::{parser::parse_program, ItemKind}, + parser::{parser::parse_program, ItemKind, ParserErrorReason}, }; #[test] @@ -360,4 +364,14 @@ mod tests { }; 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 (_, errors) = parse_program(src); + assert_eq!(errors.len(), 1); + + let reason = errors[0].reason().unwrap(); + assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index 27ba1d134bc..0f31eeee6fc 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -13,7 +13,7 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_global( &mut self, - attributes: Vec, + attributes: Vec<(Attribute, Span)>, comptime: bool, mutable: bool, ) -> LetStatement { diff --git a/compiler/noirc_frontend/src/parser/parser/module.rs b/compiler/noirc_frontend/src/parser/parser/module.rs index 853a5f933e0..5196cd4b784 100644 --- a/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -1,3 +1,5 @@ +use noirc_errors::Span; + use crate::{ ast::{Ident, ModuleDeclaration}, parser::{ItemKind, ParsedSubModule}, @@ -9,7 +11,7 @@ use super::Parser; impl<'a> Parser<'a> { pub(super) fn parse_module_or_contract( &mut self, - attributes: Vec, + attributes: Vec<(Attribute, Span)>, is_contract: bool, ) -> ItemKind { let outer_attributes = self.validate_secondary_attributes(attributes); diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index ae411ec31b9..f38b13daecd 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -10,7 +10,7 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_struct( &mut self, - attributes: Vec, + attributes: Vec<(Attribute, Span)>, visibility: ItemVisibility, start_span: Span, ) -> NoirStruct { @@ -93,7 +93,7 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, - parser::{parser::parse_program, ItemKind}, + parser::{parser::parse_program, ItemKind, ParserErrorReason}, }; #[test] @@ -193,4 +193,14 @@ mod tests { }; assert_eq!("Foo", noir_struct.name.to_string()); } + + #[test] + fn parse_error_no_function_attributes_allowed_on_struct() { + let src = "#[test] struct Foo {}"; + let (_, errors) = parse_program(src); + assert_eq!(errors.len(), 1); + + let reason = errors[0].reason().unwrap(); + assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnStruct)); + } } From 4e0cf287021b2400bb93c6c5b8af3f2e725a2efd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 14:54:14 -0300 Subject: [PATCH 040/229] Produce a couple more errors --- compiler/noirc_frontend/src/parser/errors.rs | 10 ++++ compiler/noirc_frontend/src/parser/parser.rs | 6 ++- .../src/parser/parser/attributes.rs | 7 +-- .../src/parser/parser/expression.rs | 48 +++++++++++++------ .../src/parser/parser/function.rs | 7 +-- 5 files changed, 52 insertions(+), 26 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 096aa6d31f5..9fbdb558be8 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -13,6 +13,8 @@ use super::labels::ParsingRuleLabel; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum ParserErrorReason { + #[error("Expected expression")] + ExpectedExpression, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), #[error("expected a pattern but found a type - {0}")] @@ -33,6 +35,8 @@ pub enum ParserErrorReason { ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, #[error("expected ( or < after function name")] ExpectedLeftParenOrLeftBracketAfterFunctionName, + #[error("Expected a , separating these two expressions")] + MissingCommaSeparatingExpressions, #[error("Expected a ; separating these two statements")] MissingSeparatingSemi, #[error("constrain keyword is deprecated")] @@ -105,6 +109,12 @@ 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_label(label: ParsingRuleLabel, found: Token, span: Span) -> ParserError { let mut error = ParserError::empty(found, span); error.expected_labels.insert(label); diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 860372161d3..b274eefabe8 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -7,7 +7,7 @@ use crate::{ token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; -use super::{ParsedModule, ParserError}; +use super::{ParsedModule, ParserError, ParserErrorReason}; mod attributes; mod doc_comments; @@ -299,4 +299,8 @@ impl<'a> Parser<'a> { Span::from(start_span.start()..end_span.end()) } } + + fn push_error(&mut self, reason: ParserErrorReason, span: Span) { + self.errors.push(ParserError::with_reason(reason, span)); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 0409d1ef7db..112f84edd3f 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,6 +1,6 @@ use noirc_errors::Span; -use crate::parser::{ParserError, ParserErrorReason}; +use crate::parser::ParserErrorReason; use crate::token::SecondaryAttribute; use crate::token::{Attribute, Token, TokenKind}; @@ -38,10 +38,7 @@ impl<'a> Parser<'a> { .into_iter() .filter_map(|(attribute, span)| match attribute { Attribute::Function(..) => { - self.errors.push(ParserError::with_reason( - ParserErrorReason::NoFunctionAttributesAllowedOnStruct, - span, - )); + self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnStruct, span); None } Attribute::Secondary(attr) => Some(attr), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 95edc1a7767..1007b49b5fc 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,4 +1,7 @@ -use crate::ast::{BlockExpression, Expression, ExpressionKind, Literal, Statement, StatementKind}; +use crate::{ + ast::{BlockExpression, Expression, ExpressionKind, Literal, Statement, StatementKind}, + parser::ParserErrorReason, +}; use super::Parser; @@ -28,7 +31,7 @@ impl<'a> Parser<'a> { return ExpressionKind::Block(kind); } - // TODO: parse other expressions + self.push_error(ParserErrorReason::ExpectedExpression, self.current_token_span); ExpressionKind::Error } @@ -45,19 +48,19 @@ impl<'a> Parser<'a> { let mut exprs = Vec::new(); let mut trailing_comma = false; loop { + let start_span = self.current_token_span; let expr = self.parse_expression(); if let ExpressionKind::Error = expr.kind { - // TODO: error self.eat_right_paren(); - if exprs.is_empty() { - return Some(ExpressionKind::Error); - } break; } + if !trailing_comma && !exprs.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingExpressions, start_span); + } + exprs.push(expr); trailing_comma = self.eat_commas(); - // TODO: error if no comma between exprs if self.eat_right_paren() { break; @@ -98,7 +101,7 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ExpressionKind, Literal, StatementKind}, - parser::Parser, + parser::{Parser, ParserErrorReason}, }; #[test] @@ -168,13 +171,6 @@ mod tests { assert!(!negative); } - #[test] - fn parses_unclosed_parentheses() { - let src = "("; - let expr = Parser::for_str(src).parse_expression(); - assert!(matches!(expr.kind, ExpressionKind::Error)); - } - #[test] fn parses_block_expression_with_a_single_expression() { let src = "{ 1 }"; @@ -195,4 +191,26 @@ mod tests { assert_eq!(field, 1_u128.into()); assert!(!negative); } + + #[test] + fn parses_unclosed_parentheses() { + let src = "("; + let mut parser = Parser::for_str(src); + let _ = parser.parse_expression(); + assert_eq!(parser.errors.len(), 1); + + let error = parser.errors[0].reason().unwrap(); + assert!(matches!(error, ParserErrorReason::ExpectedExpression)); + } + + #[test] + fn parses_missing_comma() { + let src = "(1 2)"; + let mut parser = Parser::for_str(src); + let _ = parser.parse_expression(); + assert_eq!(parser.errors.len(), 1); + + let error = parser.errors[0].reason().unwrap(); + assert!(matches!(error, ParserErrorReason::MissingCommaSeparatingExpressions)); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 4407442405b..6b70b31414a 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -6,7 +6,7 @@ use crate::{ BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NoirFunction, Param, UnresolvedType, UnresolvedTypeData, Visibility, }, - parser::{ParserError, ParserErrorReason}, + parser::ParserErrorReason, token::{Attribute, Attributes, Keyword, Token}, }; @@ -174,10 +174,7 @@ impl<'a> Parser<'a> { match attribute { Attribute::Function(attr) => { if primary.is_some() { - self.errors.push(ParserError::with_reason( - ParserErrorReason::MultipleFunctionAttributesFound, - span, - )); + self.push_error(ParserErrorReason::MultipleFunctionAttributesFound, span); } primary = Some(attr); } From 50455b48b46f71e95cd1c64d975a6f8cdfa56898 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 14:56:44 -0300 Subject: [PATCH 041/229] A couple more errors --- compiler/noirc_frontend/src/parser/errors.rs | 4 ++++ compiler/noirc_frontend/src/parser/parser.rs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 9fbdb558be8..d7fc3680c0c 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -15,6 +15,10 @@ use super::labels::ParsingRuleLabel; pub enum ParserErrorReason { #[error("Expected expression")] ExpectedExpression, + #[error("Unexpected ;")] + UnexpectedSemicolon, + #[error("Unexpected ,")] + UnexpectedComma, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), #[error("expected a pattern but found a type - {0}")] diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index b274eefabe8..7801b863a6e 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -223,7 +223,7 @@ impl<'a> Parser<'a> { fn eat_commas(&mut self) -> bool { if self.eat_comma() { while self.eat_comma() { - // TODO: error + self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_span); } true } else { @@ -274,7 +274,7 @@ impl<'a> Parser<'a> { fn eat_semicolons(&mut self) -> bool { if self.eat_semicolon() { while self.eat_semicolon() { - // TODO: error + self.push_error(ParserErrorReason::UnexpectedSemicolon, self.previous_token_span); } true } else { From 587a533bcecf428b69a66027faac59dd8fbddb66 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 14:57:50 -0300 Subject: [PATCH 042/229] Move error up --- compiler/noirc_frontend/src/parser/errors.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index d7fc3680c0c..6a40fc5aae6 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -19,6 +19,9 @@ pub enum ParserErrorReason { UnexpectedSemicolon, #[error("Unexpected ,")] UnexpectedComma, + #[error("Expected a , separating these two expressions")] + MissingCommaSeparatingExpressions, + #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), #[error("expected a pattern but found a type - {0}")] @@ -39,8 +42,6 @@ pub enum ParserErrorReason { ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, #[error("expected ( or < after function name")] ExpectedLeftParenOrLeftBracketAfterFunctionName, - #[error("Expected a , separating these two expressions")] - MissingCommaSeparatingExpressions, #[error("Expected a ; separating these two statements")] MissingSeparatingSemi, #[error("constrain keyword is deprecated")] From c4fe05fd1e79f6133760257f07f5812de0720889 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 15:24:16 -0300 Subject: [PATCH 043/229] Don't fail document symbol on empty function name --- tooling/lsp/src/requests/document_symbol.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tooling/lsp/src/requests/document_symbol.rs b/tooling/lsp/src/requests/document_symbol.rs index aedf35e9e62..7d908ba50a9 100644 --- a/tooling/lsp/src/requests/document_symbol.rs +++ b/tooling/lsp/src/requests/document_symbol.rs @@ -137,6 +137,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; }; From e069b902ba9ced1b2f388b8a5a20420f044d2c61 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 15:44:21 -0300 Subject: [PATCH 044/229] Some more error handling --- compiler/noirc_frontend/src/parser/errors.rs | 24 +++++++++-- compiler/noirc_frontend/src/parser/parser.rs | 4 ++ .../src/parser/parser/function.rs | 41 +++++++++++++------ .../src/parser/parser/generics.rs | 27 ++++++++++-- .../src/parser/parser/pattern.rs | 3 ++ 5 files changed, 79 insertions(+), 20 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 6a40fc5aae6..3ad560ba85d 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -15,12 +15,30 @@ use super::labels::ParsingRuleLabel; pub enum ParserErrorReason { #[error("Expected expression")] ExpectedExpression, - #[error("Unexpected ;")] + #[error("Expected pattern")] + ExpectedPattern, + #[error("Unexpected `;`")] UnexpectedSemicolon, - #[error("Unexpected ,")] + #[error("Unexpected `,`")] UnexpectedComma, - #[error("Expected a , separating these two expressions")] + #[error("Expected a `,` separating these two expressions")] MissingCommaSeparatingExpressions, + #[error("Expected a `,` separating these two parameters")] + MissingCommaSeparatingParameters, + #[error("Expected a `,` separating these two generic parameters")] + MissingCommaSeparatingGenerics, + #[error("Expected an identifier after `fn`")] + ExpectedIdentifierAfterFn, + #[error("Missing type for function parameter")] + MissingTypeForFunctionParameter, + #[error("Missing type for numeric generic")] + MissingTypeForNumericGeneric, + #[error("Expected `)`")] + ExpectedRightParen, + #[error("Expected `(`")] + ExpectedLeftParen, + #[error("Expected an integer")] + ExpectedInteger, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 7801b863a6e..0bd5108647d 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -291,6 +291,10 @@ impl<'a> Parser<'a> { } } + fn is_eof(&self) -> bool { + self.token.token() == &Token::EOF + } + fn span_since(&self, start_span: Span) -> Span { if self.current_token_span == start_span { start_span diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 6b70b31414a..6f4fb506bc8 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -45,7 +45,8 @@ impl<'a> Parser<'a> { let attributes = self.validate_attributes(attributes); let Some(name) = self.eat_ident() else { - // TODO: error + self.push_error(ParserErrorReason::ExpectedIdentifierAfterFn, self.current_token_span); + return empty_function( attributes, is_unconstrained, @@ -92,6 +93,8 @@ impl<'a> Parser<'a> { return parameters; } + let mut trailing_comma = false; + loop { if self.eat_right_paren() { break; @@ -100,9 +103,17 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let pattern = self.parse_pattern(); if self.current_token_span == start_span { - // TODO: error - self.eat_right_paren(); - break; + // An error was already produced by parse_pattern(). + // Let's try with the next token. + self.next_token(); + if self.is_eof() { + break; + } + continue; + } + + if !trailing_comma && !parameters.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); } if self.eat_colon() { @@ -116,7 +127,11 @@ impl<'a> Parser<'a> { span: self.span_since(start_span), }); } else { - // TODO: error + self.push_error( + ParserErrorReason::MissingTypeForFunctionParameter, + self.previous_token_span, + ); + parameters.push(Param { visibility: Visibility::Private, pattern, @@ -125,8 +140,7 @@ impl<'a> Parser<'a> { }); } - self.eat_commas(); - // TODO: error if no commas between parameters + trailing_comma = self.eat_commas(); } parameters @@ -145,20 +159,21 @@ impl<'a> Parser<'a> { if self.eat_left_paren() { if let Some(int) = self.eat_int() { if !self.eat_right_paren() { - // TODO: error + self.push_error( + ParserErrorReason::ExpectedRightParen, + self.current_token_span, + ); } let id = int.to_u128() as u32; return Visibility::CallData(id); } else { - // TODO: error - if !self.eat_right_paren() { - // TODO: error - } + self.push_error(ParserErrorReason::ExpectedInteger, self.current_token_span); + self.eat_right_paren(); return Visibility::CallData(0); } } else { - // TODO: error + self.push_error(ParserErrorReason::ExpectedLeftParen, self.current_token_span); return Visibility::CallData(0); } } diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 0e6eeabd5be..3254dad3c16 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -5,6 +5,7 @@ use crate::{ IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, }, + parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, }; @@ -18,11 +19,26 @@ impl<'a> Parser<'a> { return generics; } - while let Some(generic) = self.parse_generic() { + if self.eat_greater() { + // TODO: error? + return generics; + } + + let mut trailing_comma = false; + + loop { + let start_span = self.current_token_span; + let Some(generic) = self.parse_generic() else { + break; + }; + + if !trailing_comma && !generics.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); + } + generics.push(generic); - self.eat_commas(); - // TODO: error if no commas between generics + trailing_comma = self.eat_commas(); if self.eat_greater() { break; @@ -45,7 +61,10 @@ impl<'a> Parser<'a> { }; if !self.eat_colon() { - // TODO: error + self.push_error( + ParserErrorReason::MissingTypeForNumericGeneric, + self.current_token_span, + ); return Some(UnresolvedGeneric::Numeric { ident, typ: type_u32() }); } diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 192fff7d37b..58a2b6cff5e 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -1,5 +1,6 @@ use crate::{ ast::{Ident, Path, Pattern}, + parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, }; @@ -33,6 +34,8 @@ impl<'a> Parser<'a> { let mut path = self.parse_path(); if path.is_empty() { + self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); + // TODO: error return Pattern::Identifier(Ident::default()); } From 434ac88a780c49c016d7b5f1468d951a03f19283 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 16:11:16 -0300 Subject: [PATCH 045/229] Parse self pattern --- compiler/noirc_frontend/src/parser/parser.rs | 14 +++ .../src/parser/parser/function.rs | 88 +++++++++++++------ .../noirc_frontend/src/parser/parser/impls.rs | 84 +++++++++++++++++- .../src/parser/parser/pattern.rs | 36 +++++++- 4 files changed, 192 insertions(+), 30 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 0bd5108647d..a9ebb944b2a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -176,6 +176,20 @@ impl<'a> Parser<'a> { } } + fn eat_self(&mut self) -> bool { + match self.token.token() { + Token::Ident(ident) => { + if ident == "self" { + self.next_token(); + return true; + } + } + _ => (), + } + + false + } + fn eat_int_type(&mut self) -> Option { let is_int_type = matches!(self.token.token(), Token::IntType(..)); if is_int_type { diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 6f4fb506bc8..ed2eb901279 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -3,14 +3,15 @@ use noirc_errors::Span; use crate::{ ast::{ - BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, - NoirFunction, Param, UnresolvedType, UnresolvedTypeData, Visibility, + BlockExpression, FunctionDefinition, FunctionReturnType, GenericTypeArgs, Ident, + ItemVisibility, NoirFunction, Param, Path, Pattern, UnresolvedType, UnresolvedTypeData, + Visibility, }, parser::ParserErrorReason, token::{Attribute, Attributes, Keyword, Token}, }; -use super::Parser; +use super::{pattern::PatternOrSelf, Parser}; impl<'a> Parser<'a> { pub(crate) fn parse_function( @@ -101,7 +102,11 @@ impl<'a> Parser<'a> { } let start_span = self.current_token_span; - let pattern = self.parse_pattern(); + let pattern_or_self = if allow_self && parameters.is_empty() { + self.parse_pattern_or_self() + } else { + PatternOrSelf::Pattern(self.parse_pattern()) + }; if self.current_token_span == start_span { // An error was already produced by parse_pattern(). // Let's try with the next token. @@ -116,28 +121,59 @@ impl<'a> Parser<'a> { self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); } - if self.eat_colon() { - let visibility = self.parse_visibility(); + match pattern_or_self { + PatternOrSelf::Pattern(pattern) => { + if self.eat_colon() { + let visibility = self.parse_visibility(); + + let typ = self.parse_type(); + parameters.push(Param { + visibility, + pattern, + typ, + span: self.span_since(start_span), + }); + } else { + self.push_error( + ParserErrorReason::MissingTypeForFunctionParameter, + self.previous_token_span, + ); - let typ = self.parse_type(); - parameters.push(Param { - visibility, - pattern, - typ, - span: self.span_since(start_span), - }); - } else { - self.push_error( - ParserErrorReason::MissingTypeForFunctionParameter, - self.previous_token_span, - ); - - parameters.push(Param { - visibility: Visibility::Private, - pattern, - typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span: self.span_since(start_span), - }); + parameters.push(Param { + visibility: Visibility::Private, + pattern, + typ: UnresolvedType { + typ: UnresolvedTypeData::Error, + span: Span::default(), + }, + span: self.span_since(start_span), + }); + } + } + PatternOrSelf::SelfPattern(self_pattern) => { + let span = self.previous_token_span; + let ident = Ident::new("self".to_string(), span); + let path = Path::from_single("Self".to_owned(), span); + let no_args = GenericTypeArgs::default(); + let mut self_type = + UnresolvedTypeData::Named(path, no_args, true).with_span(span); + let mut pattern = Pattern::Identifier(ident); + + if self_pattern.reference { + self_type = UnresolvedTypeData::MutableReference(Box::new(self_type)) + .with_span(self.span_since(start_span)); + } else if self_pattern.mutable { + pattern = + Pattern::Mutable(Box::new(pattern), self.span_since(start_span), true); + } + + parameters.push(Param { + visibility: Visibility::Private, + pattern, + typ: self_type, + span: self.span_since(start_span), + }); + } } trailing_comma = self.eat_commas(); @@ -368,7 +404,7 @@ mod tests { fn parse_function_unclosed_parentheses() { let src = "fn foo(x: i32,"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); // TODO: there should be errors here + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Function(noir_function) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 1f89c39f5e1..f542f39e011 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -37,7 +37,6 @@ impl<'a> Parser<'a> { let visibility = self.parse_item_visibility(); let is_comptime = self.eat_keyword(Keyword::Comptime); let attributes = Vec::new(); - let allow_self = true; if self.eat_keyword(Keyword::Fn) { let method = self.parse_function( @@ -45,7 +44,7 @@ impl<'a> Parser<'a> { visibility, is_comptime, is_unconstrained, - allow_self, + true, // allow_self start_span, ); methods.push((Documented::new(method, doc_comments), self.span_since(start_span))); @@ -71,7 +70,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::ItemVisibility, + ast::{ItemVisibility, Pattern}, parser::{parser::parse_program, ItemKind}, }; @@ -132,6 +131,85 @@ mod tests { assert_eq!(method.def.visibility, ItemVisibility::Public); } + #[test] + fn parse_impl_with_self_argument() { + let src = "impl Foo { fn foo(self) {} }"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Impl(mut type_impl) = item.kind else { + panic!("Expected type impl"); + }; + 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Impl(mut type_impl) = item.kind else { + panic!("Expected type impl"); + }; + 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Impl(mut type_impl) = item.kind else { + panic!("Expected type impl"); + }; + 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_empty_impl_missing_right_brace() { let src = "impl Foo {"; diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 58a2b6cff5e..ece098c7dfe 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -1,3 +1,5 @@ +use noirc_errors::Span; + use crate::{ ast::{Ident, Path, Pattern}, parser::ParserErrorReason, @@ -6,11 +8,43 @@ use crate::{ use super::Parser; +pub(crate) enum PatternOrSelf { + Pattern(Pattern), + SelfPattern(SelfPattern), +} + +pub(crate) struct SelfPattern { + pub(crate) reference: bool, + pub(crate) mutable: bool, +} + impl<'a> Parser<'a> { pub(crate) fn parse_pattern(&mut self) -> Pattern { let start_span = self.current_token_span; + let mutable = self.eat_keyword(Keyword::Mut); + self.parse_pattern_after_modifiers(mutable, start_span) + } + + pub(crate) fn parse_pattern_or_self(&mut self) -> PatternOrSelf { + let start_span = self.current_token_span; + let reference = self.eat(Token::Ampersand); let mutable = self.eat_keyword(Keyword::Mut); + + if self.eat_self() { + // TODO: error if reference but not mutable + PatternOrSelf::SelfPattern(SelfPattern { reference, mutable }) + } else { + // TODO: error if reference is true + PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(mutable, start_span)) + } + } + + pub(crate) fn parse_pattern_after_modifiers( + &mut self, + mutable: bool, + start_span: Span, + ) -> Pattern { let pattern = self.parse_pattern_no_mut(); if mutable { Pattern::Mutable( @@ -170,7 +204,7 @@ mod tests { let src = "(foo,"; let mut parser = Parser::for_str(src); let typ = parser.parse_pattern(); - assert!(parser.errors.is_empty()); // TODO: there should be an error here + assert_eq!(parser.errors.len(), 1); let Pattern::Tuple(patterns, _) = typ else { panic!("Expected a tuple pattern") }; assert_eq!(patterns.len(), 1); } From 60316b19f931f57a59265852bb969e43640865c3 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 16:33:52 -0300 Subject: [PATCH 046/229] Parse empty trait impl --- .../noirc_frontend/src/parser/parser/impls.rs | 77 ++++++++++++++++++- .../noirc_frontend/src/parser/parser/item.rs | 7 +- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index f542f39e011..a5d678b5c49 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -1,24 +1,61 @@ use noirc_errors::Span; use crate::{ - ast::{Documented, NoirFunction, TypeImpl}, + ast::{ + Documented, GenericTypeArgs, NoirFunction, NoirTraitImpl, Path, TypeImpl, + UnresolvedGeneric, UnresolvedTypeData, + }, token::Keyword, }; use super::Parser; +pub(crate) enum Impl { + Impl(TypeImpl), + TraitImpl(NoirTraitImpl), +} + impl<'a> Parser<'a> { - pub(crate) fn parse_impl(&mut self) -> TypeImpl { + 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(); let type_span = self.span_since(type_span_start); + if self.eat_keyword(Keyword::For) { + if let UnresolvedTypeData::Named(trait_name, _, _) = object_type.typ { + return Impl::TraitImpl(self.parse_trait_impl(generics, trait_name)); + } else { + // TODO: error, but we continue parsing the type and assume this is going to be a regular impl + self.parse_type(); + }; + } + let where_clause = self.parse_where_clause(); let methods = self.parse_impl_body(); - TypeImpl { object_type, type_span, generics, where_clause, methods } + Impl::Impl(TypeImpl { object_type, type_span, generics, where_clause, methods }) + } + + fn parse_trait_impl( + &mut self, + impl_generics: Vec, + trait_name: Path, + ) -> NoirTraitImpl { + // TODO: parse generics + // TODO: parse body + let trait_generics = GenericTypeArgs::default(); + let object_type = self.parse_type(); + let where_clause = self.parse_where_clause(); + NoirTraitImpl { + impl_generics, + trait_name, + trait_generics, + object_type, + where_clause, + items: Vec::new(), + } } fn parse_impl_body(&mut self) -> Vec<(Documented, Span)> { @@ -70,7 +107,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, Pattern}, + ast::{ItemVisibility, Pattern, UnresolvedTypeData}, parser::{parser::parse_program, ItemKind}, }; @@ -235,4 +272,36 @@ mod tests { }; assert_eq!(type_impl.object_type.to_string(), "Foo"); } + + #[test] + fn parse_empty_trait_impl() { + let src = "impl Foo for Field {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::TraitImpl(trait_impl) = &item.kind else { + panic!("Expected trait impl"); + }; + 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 (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::TraitImpl(trait_impl) = &item.kind else { + panic!("Expected trait impl"); + }; + 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); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index ae2d7575a68..fe3256e764e 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -3,7 +3,7 @@ use crate::{ token::Keyword, }; -use super::Parser; +use super::{impls::Impl, Parser}; impl<'a> Parser<'a> { pub(crate) fn parse_items(&mut self) -> Vec { @@ -64,7 +64,10 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Impl) { // TODO: error if there's comptime or mutable or unconstrained - return Some(ItemKind::Impl(self.parse_impl())); + 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::Global) { From 78a430c6e56ef6f4f61b1e888de2f6b799f2fced Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 16:43:21 -0300 Subject: [PATCH 047/229] Parse trait impl method --- .../noirc_frontend/src/parser/parser/impls.rs | 80 ++++++++++++++++++- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index a5d678b5c49..990c62cc211 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -2,8 +2,8 @@ use noirc_errors::Span; use crate::{ ast::{ - Documented, GenericTypeArgs, NoirFunction, NoirTraitImpl, Path, TypeImpl, - UnresolvedGeneric, UnresolvedTypeData, + Documented, GenericTypeArgs, ItemVisibility, NoirFunction, NoirTraitImpl, Path, + TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedTypeData, }, token::Keyword, }; @@ -48,13 +48,15 @@ impl<'a> Parser<'a> { let trait_generics = GenericTypeArgs::default(); let object_type = self.parse_type(); let where_clause = self.parse_where_clause(); + let items = self.parse_trait_impl_items(); + NoirTraitImpl { impl_generics, trait_name, trait_generics, object_type, where_clause, - items: Vec::new(), + items, } } @@ -102,12 +104,61 @@ impl<'a> Parser<'a> { methods } + + fn parse_trait_impl_items(&mut self) -> Vec> { + let mut items = Vec::new(); + + if !self.eat_left_brace() { + // TODO: error + return items; + } + + loop { + // TODO: maybe require visibility to always come first + let doc_comments = self.parse_outer_doc_comments(); + let start_span = self.current_token_span; + let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); + if self.parse_item_visibility() != ItemVisibility::Private { + // TODO: error + } + let is_comptime = self.eat_keyword(Keyword::Comptime); + let attributes = Vec::new(); + + if self.eat_keyword(Keyword::Fn) { + let noir_function = self.parse_function( + attributes, + ItemVisibility::Public, + is_comptime, + is_unconstrained, + true, // allow_self + start_span, + ); + let item_kind = TraitImplItemKind::Function(noir_function); + let item = TraitImplItem { kind: item_kind, span: self.span_since(start_span) }; + items.push(Documented::new(item, doc_comments)); + + if self.eat_right_brace() { + break; + } + } else { + // TODO: error if visibility, unconstrained or comptime were found + + if !self.eat_right_brace() { + // TODO: error + } + + break; + } + } + + items + } } #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, Pattern, UnresolvedTypeData}, + ast::{ItemVisibility, Pattern, TraitImplItemKind, UnresolvedTypeData}, parser::{parser::parse_program, ItemKind}, }; @@ -304,4 +355,25 @@ mod tests { 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::TraitImpl(mut trait_impl) = item.kind else { + panic!("Expected trait impl"); + }; + 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); + } } From 18910a966c75fa6f12f6873028e0bab0396b6eb8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 16:44:12 -0300 Subject: [PATCH 048/229] Clippy --- compiler/noirc_frontend/src/parser/parser.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index a9ebb944b2a..bb7f00e9f38 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -177,14 +177,11 @@ impl<'a> Parser<'a> { } fn eat_self(&mut self) -> bool { - match self.token.token() { - Token::Ident(ident) => { - if ident == "self" { - self.next_token(); - return true; - } + if let Token::Ident(ident) = self.token.token() { + if ident == "self" { + self.next_token(); + return true; } - _ => (), } false From 11d2e702ce7580610fef26699c81199672c752b2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 21:07:42 -0300 Subject: [PATCH 049/229] Parse turbofish in Path --- .../noirc_frontend/src/parser/parser/path.rs | 61 +++++++++++++++++-- .../src/parser/parser/use_tree.rs | 5 +- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 9a9856e8621..1e52d0d135a 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,7 +1,7 @@ use noirc_errors::Span; use crate::{ - ast::{Path, PathKind, PathSegment}, + ast::{Path, PathKind, PathSegment, UnresolvedType}, token::{Keyword, TokenKind}, }; @@ -32,12 +32,13 @@ impl<'a> Parser<'a> { // TODO: error } - self.parse_path_after_kind(kind, start_span) + self.parse_path_after_kind(kind, allow_turbofish, start_span) } pub(super) fn parse_path_after_kind( &mut self, kind: PathKind, + allow_turbofish: bool, start_span: Span, ) -> (Path, bool) { let mut trailing_double_colon = false; @@ -46,8 +47,23 @@ impl<'a> Parser<'a> { if self.token.kind() == TokenKind::Ident { while let Some(ident) = self.eat_ident() { let span = ident.span(); - segments.push(PathSegment { ident, generics: None, span }); - if self.eat_double_colon() { + + let mut has_double_colon = self.eat_double_colon(); + + let generics = if has_double_colon && allow_turbofish { + if let Some(generics) = self.parse_path_generics() { + has_double_colon = self.eat_double_colon(); + Some(generics) + } else { + None + } + } else { + None + }; + + segments.push(PathSegment { ident, generics, span }); + + if has_double_colon { trailing_double_colon = true; } else { trailing_double_colon = false; @@ -63,6 +79,28 @@ impl<'a> Parser<'a> { (Path { segments, kind, span }, trailing_double_colon) } + fn parse_path_generics(&mut self) -> Option> { + if !self.eat_less() { + return None; + } + + let mut generics = Vec::new(); + if self.eat_greater() { + // TODO: error + } else { + loop { + let typ = self.parse_type(); + generics.push(typ); + self.eat_commas(); + + if self.eat_greater() { + break; + } + } + } + Some(generics) + } + pub(super) fn parse_path_kind(&mut self) -> PathKind { if self.eat_keyword(Keyword::Crate) { PathKind::Crate @@ -151,4 +189,19 @@ mod tests { assert!(path.segments[0].generics.is_none()); assert_eq!(path.span.end() as usize, src.len()); } + + #[test] + fn parses_with_turbofish() { + let src = "foo::::bar"; + let mut path = Parser::for_str(src).parse_path(); + assert_eq!(path.kind, PathKind::Plain); + assert_eq!(path.segments.len(), 2); + 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()); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 943eacd6b15..a6bca2b43d3 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -28,7 +28,10 @@ impl<'a> Parser<'a> { start_span: Span, kind: PathKind, ) -> UseTree { - let (prefix, mut trailing_double_colon) = self.parse_path_after_kind(kind, start_span); + let (prefix, mut trailing_double_colon) = self.parse_path_after_kind( + kind, false, // allow turbofish + start_span, + ); if prefix.segments.is_empty() && kind != PathKind::Plain { trailing_double_colon = true; From dab7104e86adfa53421b46058c249330ebda8440 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 21:41:01 -0300 Subject: [PATCH 050/229] Parse generic type args --- .../src/parser/parser/generics.rs | 75 ++++++++++++++++++- .../noirc_frontend/src/parser/parser/impls.rs | 65 ++++++++++------ .../noirc_frontend/src/parser/parser/path.rs | 18 ++++- .../noirc_frontend/src/parser/parser/types.rs | 71 ++++++++++++++---- 4 files changed, 186 insertions(+), 43 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 3254dad3c16..d0da27857b6 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -2,8 +2,8 @@ use noirc_errors::Span; use crate::{ ast::{ - IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, - UnresolvedTypeData, + GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, + UnresolvedType, UnresolvedTypeData, }, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, @@ -87,6 +87,48 @@ impl<'a> Parser<'a> { None } + + 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 mut trailing_comma = false; + loop { + let start_span = self.current_token_span; + if let Some(ident) = self.eat_ident() { + if !trailing_comma && !generic_type_args.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); + } + + if self.eat_assign() { + let typ = self.parse_type(); + generic_type_args.named_args.push((ident, typ)); + } else { + let typ = self.parse_path_type_after_ident(ident); + generic_type_args.ordered_args.push(typ); + } + } else { + let typ = self.parse_type(); + if self.current_token_span == start_span { + self.eat_greater(); + break; + } + + if !trailing_comma && !generic_type_args.is_empty() { + println!("1"); + self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); + } + + generic_type_args.ordered_args.push(typ); + } + + trailing_comma = self.eat_commas(); + } + + generic_type_args + } } fn type_u32() -> UnresolvedType { @@ -132,4 +174,33 @@ mod tests { UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) ) } + + #[test] + fn parses_no_generic_type_args() { + let src = "1"; + let generics = Parser::for_str(src).parse_generic_type_args(); + assert!(generics.is_empty()); + } + + #[test] + fn parses_generic_type_args() { + let src = ""; + let generics = Parser::for_str(src).parse_generic_type_args(); + 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 = Parser::for_str(src).parse_generic_type_args(); + 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); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 990c62cc211..b0f00da00a9 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -24,8 +24,12 @@ impl<'a> Parser<'a> { let type_span = self.span_since(type_span_start); if self.eat_keyword(Keyword::For) { - if let UnresolvedTypeData::Named(trait_name, _, _) = object_type.typ { - return Impl::TraitImpl(self.parse_trait_impl(generics, trait_name)); + if let UnresolvedTypeData::Named(trait_name, trait_generics, _) = object_type.typ { + return Impl::TraitImpl(self.parse_trait_impl( + generics, + trait_generics, + trait_name, + )); } else { // TODO: error, but we continue parsing the type and assume this is going to be a regular impl self.parse_type(); @@ -38,28 +42,6 @@ impl<'a> Parser<'a> { Impl::Impl(TypeImpl { object_type, type_span, generics, where_clause, methods }) } - fn parse_trait_impl( - &mut self, - impl_generics: Vec, - trait_name: Path, - ) -> NoirTraitImpl { - // TODO: parse generics - // TODO: parse body - let trait_generics = GenericTypeArgs::default(); - let object_type = self.parse_type(); - let where_clause = self.parse_where_clause(); - let items = self.parse_trait_impl_items(); - - NoirTraitImpl { - impl_generics, - trait_name, - trait_generics, - object_type, - where_clause, - items, - } - } - fn parse_impl_body(&mut self) -> Vec<(Documented, Span)> { let mut methods = Vec::new(); @@ -92,6 +74,7 @@ impl<'a> Parser<'a> { break; } } else { + // TODO: parse Type and Constant // TODO: error if visibility, unconstrained or comptime were found if !self.eat_right_brace() { @@ -105,6 +88,26 @@ impl<'a> Parser<'a> { methods } + fn parse_trait_impl( + &mut self, + impl_generics: Vec, + trait_generics: GenericTypeArgs, + trait_name: Path, + ) -> NoirTraitImpl { + let object_type = self.parse_type(); + let where_clause = self.parse_where_clause(); + let items = self.parse_trait_impl_items(); + + NoirTraitImpl { + impl_generics, + trait_name, + trait_generics, + object_type, + where_clause, + items, + } + } + fn parse_trait_impl_items(&mut self) -> Vec> { let mut items = Vec::new(); @@ -376,4 +379,18 @@ mod tests { 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 (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::TraitImpl(trait_impl) = item.kind else { + panic!("Expected trait impl"); + }; + assert_eq!(trait_impl.trait_name.to_string(), "Foo"); + assert!(!trait_impl.trait_generics.is_empty()); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 1e52d0d135a..81afd68c213 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,7 +1,7 @@ use noirc_errors::Span; use crate::{ - ast::{Path, PathKind, PathSegment, UnresolvedType}, + ast::{Ident, Path, PathKind, PathSegment, UnresolvedType}, token::{Keyword, TokenKind}, }; @@ -79,6 +79,22 @@ impl<'a> Parser<'a> { (Path { segments, kind, span }, trailing_double_colon) } + pub(super) fn parse_path_no_turbofish_after_ident(&mut self, ident: Ident) -> Path { + let start_span = ident.span(); + let mut segments = vec![PathSegment::from(ident)]; + + while self.eat_double_colon() { + if let Some(ident) = self.eat_ident() { + segments.push(PathSegment::from(ident)); + } else { + // TODO: error (trailing double colon in path) + break; + } + } + + Path { segments, kind: PathKind::Plain, span: self.span_since(start_span) } + } + fn parse_path_generics(&mut self) -> Option> { if !self.eat_less() { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index dd5e9cc6023..dfc7b9c47a0 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,7 +1,7 @@ use noirc_errors::Span; use crate::{ - ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData}, + ast::{Ident, UnresolvedType, UnresolvedTypeData}, token::{Keyword, Token}, }; @@ -22,41 +22,72 @@ impl<'a> Parser<'a> { return typ; } + if let Some(typ) = self.parse_bool_type() { + return typ; + } + + if let Some(typ) = self.parse_field_type() { + return typ; + } + + if let Some(typ) = self.parse_int_type() { + return typ; + } + + if let Some(typ) = self.parses_mutable_reference_type() { + return typ; + } + + let path = self.parse_path_no_turbofish(); + if !path.is_empty() { + let generics = self.parse_generic_type_args(); + return UnresolvedTypeData::Named(path, generics, false); + } + + // TODO: parse more types + + UnresolvedTypeData::Error + } + + fn parse_bool_type(&mut self) -> Option { if self.eat_keyword(Keyword::Bool) { - return UnresolvedTypeData::Bool; + return Some(UnresolvedTypeData::Bool); } + None + } + + fn parse_field_type(&mut self) -> Option { if self.eat_keyword(Keyword::Field) { - return UnresolvedTypeData::FieldElement; + return Some(UnresolvedTypeData::FieldElement); } + None + } + + fn parse_int_type(&mut self) -> Option { if let Some(int_type) = self.eat_int_type() { - return match UnresolvedTypeData::from_int_token(int_type) { + return Some(match UnresolvedTypeData::from_int_token(int_type) { Ok(typ) => typ, Err(_) => { // TODO: error UnresolvedTypeData::Error } - }; + }); } + None + } + + fn parses_mutable_reference_type(&mut self) -> Option { if self.eat(Token::Ampersand) { if !self.eat_keyword(Keyword::Mut) { // TODO: error } - return UnresolvedTypeData::MutableReference(Box::new(self.parse_type())); + return Some(UnresolvedTypeData::MutableReference(Box::new(self.parse_type()))); }; - let path = self.parse_path_no_turbofish(); - if !path.is_empty() { - // TODO: parse generics - let generics = GenericTypeArgs::default(); - return UnresolvedTypeData::Named(path, generics, false); - } - - // TODO: parse more types - - UnresolvedTypeData::Error + None } fn parse_parentheses_type(&mut self) -> Option { @@ -96,6 +127,14 @@ impl<'a> Parser<'a> { }) } + pub(super) fn parse_path_type_after_ident(&mut self, ident: Ident) -> UnresolvedType { + let start_span = ident.span(); + let path = self.parse_path_no_turbofish_after_ident(ident); + let generics = self.parse_generic_type_args(); + let typ = UnresolvedTypeData::Named(path, generics, false); + UnresolvedType { typ, span: self.span_since(start_span) } + } + pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { if self.eat_colon() { self.parse_type() From d37d13ce0acf583d9ccda539183ce086ad012897 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 21:58:49 -0300 Subject: [PATCH 051/229] Parse trait impl type --- .../noirc_frontend/src/parser/parser/impls.rs | 120 ++++++++++++++---- 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index b0f00da00a9..3c9f05eab9f 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -2,8 +2,9 @@ use noirc_errors::Span; use crate::{ ast::{ - Documented, GenericTypeArgs, ItemVisibility, NoirFunction, NoirTraitImpl, Path, - TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedTypeData, + Documented, GenericTypeArgs, Ident, ItemVisibility, NoirFunction, NoirTraitImpl, Path, + TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedType, + UnresolvedTypeData, }, token::Keyword, }; @@ -118,44 +119,90 @@ impl<'a> Parser<'a> { loop { // TODO: maybe require visibility to always come first - let doc_comments = self.parse_outer_doc_comments(); let start_span = self.current_token_span; - let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); - if self.parse_item_visibility() != ItemVisibility::Private { - // TODO: error - } - let is_comptime = self.eat_keyword(Keyword::Comptime); - let attributes = Vec::new(); + let doc_comments = self.parse_outer_doc_comments(); - if self.eat_keyword(Keyword::Fn) { - let noir_function = self.parse_function( - attributes, - ItemVisibility::Public, - is_comptime, - is_unconstrained, - true, // allow_self - start_span, - ); - let item_kind = TraitImplItemKind::Function(noir_function); - let item = TraitImplItem { kind: item_kind, span: self.span_since(start_span) }; + if let Some(kind) = self.parse_trait_impl_item_kind(start_span) { + let item = TraitImplItem { kind, span: self.span_since(start_span) }; items.push(Documented::new(item, doc_comments)); if self.eat_right_brace() { break; } } else { - // TODO: error if visibility, unconstrained or comptime were found - - if !self.eat_right_brace() { - // TODO: error + // TODO: error + if self.is_eof() || self.eat_right_brace() { + break; + } else { + // Keep going + self.next_token(); } - - break; } } items } + + fn parse_trait_impl_item_kind(&mut self, start_span: Span) -> Option { + if let Some(kind) = self.parse_trait_impl_type() { + return Some(kind); + } + + if let Some(kind) = self.parse_trait_impl_function(start_span) { + return Some(kind); + } + + None + } + + fn parse_trait_impl_function(&mut self, start_span: Span) -> Option { + let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); + if self.parse_item_visibility() != ItemVisibility::Private { + // TODO: error + } + let is_comptime = self.eat_keyword(Keyword::Comptime); + let attributes = Vec::new(); + + if !self.eat_keyword(Keyword::Fn) { + // TODO: error if unconstrained, visibility or comptime + return None; + } + + let noir_function = self.parse_function( + attributes, + ItemVisibility::Public, + is_comptime, + is_unconstrained, + true, // allow_self + start_span, + ); + Some(TraitImplItemKind::Function(noir_function)) + } + + fn parse_trait_impl_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::Type) { + return None; + } + + let Some(name) = self.eat_ident() else { + // TODO: error + 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() + } else { + UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() } + }; + + self.eat_semicolons(); + + Some(TraitImplItemKind::Type { name, alias }) + } } #[cfg(test)] @@ -393,4 +440,25 @@ mod tests { 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::TraitImpl(mut trait_impl) = item.kind else { + panic!("Expected trait impl"); + }; + 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"); + } } From ed82959c3917885aa8fd7584f036a52cf747dd60 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 22:26:17 -0300 Subject: [PATCH 052/229] Parse where clause --- compiler/noirc_frontend/src/parser/errors.rs | 4 + compiler/noirc_frontend/src/parser/parser.rs | 28 ++--- .../src/parser/parser/where_clause.rs | 116 +++++++++++++++++- 3 files changed, 131 insertions(+), 17 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 3ad560ba85d..ac7ba44d997 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -27,6 +27,10 @@ pub enum ParserErrorReason { MissingCommaSeparatingParameters, #[error("Expected a `,` separating these two generic parameters")] MissingCommaSeparatingGenerics, + #[error("Expected a `,` separating these two trait bounds")] + MissingCommaSeparatingTraitBounds, + #[error("Expected a `+` separating these two trait bounds")] + MissingPlusSeparatingTraitBounds, #[error("Expected an identifier after `fn`")] ExpectedIdentifierAfterFn, #[error("Missing type for function parameter")] diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index bb7f00e9f38..3f621d0c45b 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -2,7 +2,7 @@ use acvm::FieldElement; use noirc_errors::Span; use crate::{ - ast::{Ident, LValue, Statement, TraitBound}, + ast::{Ident, LValue, Statement}, lexer::{Lexer, SpannedTokenResult}, token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; @@ -108,10 +108,6 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { - todo!("Parser") - } - pub(crate) fn parse_statement(&mut self) -> Statement { todo!("Parser") } @@ -246,6 +242,17 @@ impl<'a> Parser<'a> { 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 + } + } + fn eat_colon(&mut self) -> bool { self.eat(Token::Colon) } @@ -282,15 +289,8 @@ impl<'a> Parser<'a> { self.eat(Token::Assign) } - 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 - } + fn eat_plus(&mut self) -> bool { + self.eat(Token::Plus) } fn eat(&mut self, token: Token) -> bool { diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 5324482734f..4838396eb94 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,10 +1,120 @@ -use crate::ast::UnresolvedTraitConstraint; +use crate::{ + ast::{TraitBound, UnresolvedTraitConstraint}, + parser::ParserErrorReason, + token::Keyword, +}; use super::Parser; impl<'a> Parser<'a> { pub(super) fn parse_where_clause(&mut self) -> Vec { - // TODO: parse where clause - Vec::new() + let mut constraints = Vec::new(); + + if !self.eat_keyword(Keyword::Where) { + return constraints; + } + + let mut trailing_comma = false; + loop { + let start_span = self.current_token_span; + let typ = self.parse_type(); + if self.current_token_span == start_span { + break; + } + + if !trailing_comma && !constraints.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingTraitBounds, start_span); + } + + if self.eat_colon() { + let trait_bounds = self.parse_trait_bounds(); + for trait_bound in trait_bounds { + constraints.push(UnresolvedTraitConstraint { typ: typ.clone(), trait_bound }); + } + } else { + // TODO: error + } + + trailing_comma = self.eat_commas(); + } + + if constraints.is_empty() { + // TODO: error + } + + constraints + } + + pub(super) fn parse_trait_bounds(&mut self) -> Vec { + let mut bounds = Vec::new(); + + let mut trailing_plus = false; + loop { + let start_span = self.current_token_span; + let bound = self.parse_trait_bound(); + if self.current_token_span == start_span { + break; + } + + if !trailing_plus && !bounds.is_empty() { + self.push_error(ParserErrorReason::MissingPlusSeparatingTraitBounds, start_span); + } + + bounds.push(bound); + + trailing_plus = self.eat_plus(); + } + + bounds + } + + pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { + let trait_path = self.parse_path_no_turbofish(); + let trait_generics = self.parse_generic_type_args(); + TraitBound { trait_path, trait_generics, trait_id: None } + } +} + +#[cfg(test)] +mod tests { + use crate::parser::Parser; + + #[test] + fn parses_no_where_clause() { + let src = "{"; + let constraints = Parser::for_str(src).parse_where_clause(); + assert!(constraints.is_empty()); + } + + #[test] + fn parses_one_where_clause_with_two_constraints() { + let src = "where Foo: Bar + Baz"; + let mut constraints = Parser::for_str(src).parse_where_clause(); + 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 = Parser::for_str(src).parse_where_clause(); + 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"); } } From 4f09b09cc204e9c88bd0cc31dc6c6f23862755e8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 22:26:22 -0300 Subject: [PATCH 053/229] Handle more empty cases in document symbol --- tooling/lsp/src/requests/document_symbol.rs | 34 ++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tooling/lsp/src/requests/document_symbol.rs b/tooling/lsp/src/requests/document_symbol.rs index 7d908ba50a9..5948d73a725 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; }; @@ -221,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; }; @@ -263,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; }; @@ -316,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 } @@ -408,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; @@ -439,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; }; @@ -473,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; }; @@ -483,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, From 70105764230ccbd66bca990b60477b8a8b66f323 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 22:27:03 -0300 Subject: [PATCH 054/229] Clippy --- compiler/noirc_frontend/src/parser/parser/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index ed2eb901279..bc5763f1a9f 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { let where_clause = self.parse_where_clause(); - let body = self.parse_block_expression().unwrap_or_else(|| empty_body()); + let body = self.parse_block_expression().unwrap_or_else(empty_body); FunctionDefinition { name, From f013d5081efb51d832c180b234c407e3b21728dc Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 26 Sep 2024 22:36:19 -0300 Subject: [PATCH 055/229] Adjust some spans --- .../src/parser/parser/function.rs | 18 +++++------------- .../noirc_frontend/src/parser/parser/impls.rs | 10 ++++------ .../noirc_frontend/src/parser/parser/item.rs | 4 +--- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index bc5763f1a9f..52461f24f2c 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -21,7 +21,6 @@ impl<'a> Parser<'a> { is_comptime: bool, is_unconstrained: bool, allow_self: bool, - start_span: Span, ) -> NoirFunction { self.parse_function_definition( attributes, @@ -29,7 +28,6 @@ impl<'a> Parser<'a> { is_comptime, is_unconstrained, allow_self, - start_span, ) .into() } @@ -41,20 +39,13 @@ impl<'a> Parser<'a> { is_comptime: bool, is_unconstrained: bool, allow_self: bool, - start_span: Span, ) -> FunctionDefinition { let attributes = self.validate_attributes(attributes); let Some(name) = self.eat_ident() else { self.push_error(ParserErrorReason::ExpectedIdentifierAfterFn, self.current_token_span); - return empty_function( - attributes, - is_unconstrained, - is_comptime, - visibility, - start_span, - ); + return empty_function(attributes, is_unconstrained, is_comptime, visibility); }; let generics = self.parse_generics(); @@ -69,7 +60,9 @@ impl<'a> Parser<'a> { let where_clause = self.parse_where_clause(); + let body_start_span = self.current_token_span; let body = self.parse_block_expression().unwrap_or_else(empty_body); + let body_span = self.span_since(body_start_span); FunctionDefinition { name, @@ -80,7 +73,7 @@ impl<'a> Parser<'a> { generics, parameters, body, - span: start_span, + span: body_span, where_clause, return_type, return_visibility, @@ -242,7 +235,6 @@ fn empty_function( is_unconstrained: bool, is_comptime: bool, visibility: ItemVisibility, - start_span: Span, ) -> FunctionDefinition { FunctionDefinition { name: Ident::default(), @@ -253,7 +245,7 @@ fn empty_function( generics: Vec::new(), parameters: Vec::new(), body: empty_body(), - span: start_span, + span: Span::default(), where_clause: Vec::new(), return_type: FunctionReturnType::Default(Span::default()), return_visibility: Visibility::Private, diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 3c9f05eab9f..2ca8d4d2323 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -67,7 +67,6 @@ impl<'a> Parser<'a> { is_comptime, is_unconstrained, true, // allow_self - start_span, ); methods.push((Documented::new(method, doc_comments), self.span_since(start_span))); @@ -122,7 +121,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let doc_comments = self.parse_outer_doc_comments(); - if let Some(kind) = self.parse_trait_impl_item_kind(start_span) { + if let Some(kind) = self.parse_trait_impl_item_kind() { let item = TraitImplItem { kind, span: self.span_since(start_span) }; items.push(Documented::new(item, doc_comments)); @@ -143,19 +142,19 @@ impl<'a> Parser<'a> { items } - fn parse_trait_impl_item_kind(&mut self, start_span: Span) -> Option { + 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_function(start_span) { + if let Some(kind) = self.parse_trait_impl_function() { return Some(kind); } None } - fn parse_trait_impl_function(&mut self, start_span: Span) -> Option { + fn parse_trait_impl_function(&mut self) -> Option { let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); if self.parse_item_visibility() != ItemVisibility::Private { // TODO: error @@ -174,7 +173,6 @@ impl<'a> Parser<'a> { is_comptime, is_unconstrained, true, // allow_self - start_span, ); Some(TraitImplItemKind::Function(noir_function)) } diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index fe3256e764e..a8ecc724187 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -17,9 +17,8 @@ impl<'a> Parser<'a> { } fn parse_item(&mut self) -> Option { - let doc_comments = self.parse_outer_doc_comments(); - 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); @@ -88,7 +87,6 @@ impl<'a> Parser<'a> { comptime, unconstrained, false, // allow_self - start_span, ))); } From 4c90f4311aa45f2cace4cb501fd1c54b4931769a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 06:56:32 -0300 Subject: [PATCH 056/229] Parse empty traits --- compiler/noirc_frontend/src/parser/parser.rs | 1 + .../noirc_frontend/src/parser/parser/item.rs | 7 + .../src/parser/parser/traits.rs | 120 ++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 compiler/noirc_frontend/src/parser/parser/traits.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 3f621d0c45b..fcddbd7f98a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -22,6 +22,7 @@ mod module; mod path; mod pattern; mod structs; +mod traits; mod type_alias; mod types; mod use_tree; diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index a8ecc724187..afa238a21c9 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -69,6 +69,11 @@ impl<'a> Parser<'a> { }); } + if self.eat_keyword(Keyword::Trait) { + // TODO: error if there's comptime or mutable or unconstrained + return Some(ItemKind::Trait(self.parse_trait(attributes, visibility, start_span))); + } + if self.eat_keyword(Keyword::Global) { // TODO: error if there's unconstrained return Some(ItemKind::Global(self.parse_global(attributes, comptime, mutable))); @@ -90,6 +95,8 @@ impl<'a> Parser<'a> { ))); } + // TODO: error + None } diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs new file mode 100644 index 00000000000..8f3f982f1b3 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -0,0 +1,120 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Documented, Ident, ItemVisibility, NoirTrait, TraitItem}, + token::{Attribute, SecondaryAttribute}, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + 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 { + // TODO: error + return empty_trait(attributes, visibility, self.span_since(start_span)); + }; + + let generics = self.parse_generics(); + let where_clause = self.parse_where_clause(); + let items = self.parse_trait_items(); + + NoirTrait { + name, + generics, + where_clause, + span: self.span_since(start_span), + items, + attributes, + visibility, + } + } + + fn parse_trait_items(&mut self) -> Vec> { + let mut items = Vec::new(); + + if !self.eat_left_brace() { + // TODO: error + return items; + } + + self.eat_right_brace(); + + items + } +} + +fn empty_trait( + attributes: Vec, + visibility: ItemVisibility, + span: Span, +) -> NoirTrait { + return NoirTrait { + name: Ident::default(), + generics: Vec::new(), + where_clause: Vec::new(), + span, + items: Vec::new(), + attributes, + visibility, + }; +} + +#[cfg(test)] +mod tests { + use crate::parser::{parser::parse_program, ItemKind}; + + #[test] + fn parse_empty_trait() { + let src = "trait Foo {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Trait(noir_trait) = &item.kind else { + panic!("Expected trait"); + }; + 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_trait_with_generics() { + let src = "trait Foo {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Trait(noir_trait) = &item.kind else { + panic!("Expected trait"); + }; + 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()); + } + + #[test] + fn parse_trait_with_where_clause() { + let src = "trait Foo where A: Z {}"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Trait(noir_trait) = &item.kind else { + panic!("Expected trait"); + }; + 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()); + } +} From 1020735bfe3f68ce06d3f231d87d119b5bd7eb53 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 07:06:03 -0300 Subject: [PATCH 057/229] Parse trait type --- .../src/parser/parser/traits.rs | 70 ++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 8f3f982f1b3..679312a7603 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Documented, Ident, ItemVisibility, NoirTrait, TraitItem}, - token::{Attribute, SecondaryAttribute}, + token::{Attribute, Keyword, SecondaryAttribute}, }; use super::Parser; @@ -44,10 +44,52 @@ impl<'a> Parser<'a> { return items; } - self.eat_right_brace(); + loop { + let doc_comments = self.parse_outer_doc_comments(); + + if let Some(item) = self.parse_trait_item() { + items.push(Documented::new(item, doc_comments)); + + if self.eat_right_brace() { + break; + } + } else { + // TODO: error + if self.is_eof() || self.eat_right_brace() { + break; + } else { + // Keep going + self.next_token(); + } + } + } items } + + fn parse_trait_item(&mut self) -> Option { + if let Some(kind) = self.parse_trait_type() { + return Some(kind); + } + + None + } + + fn parse_trait_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::Type) { + return None; + } + + let name = self.eat_ident(); + if name.is_none() { + // TODO: error + } + + self.eat_semicolons(); + + let name = name.unwrap_or_default(); + Some(TraitItem::Type { name }) + } } fn empty_trait( @@ -68,7 +110,10 @@ fn empty_trait( #[cfg(test)] mod tests { - use crate::parser::{parser::parse_program, ItemKind}; + use crate::{ + ast::TraitItem, + parser::{parser::parse_program, ItemKind}, + }; #[test] fn parse_empty_trait() { @@ -117,4 +162,23 @@ mod tests { assert_eq!(noir_trait.where_clause.len(), 1); assert!(noir_trait.items.is_empty()); } + + #[test] + fn parse_trait_with_type() { + let src = "trait Foo { type Elem; }"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Trait(mut noir_trait) = item.kind else { + panic!("Expected trait"); + }; + 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!(name.to_string(), "Elem"); + } } From 4cb842bfac8cc6e9a8f41ccf1e0370e81196ab83 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 07:12:59 -0300 Subject: [PATCH 058/229] Parse trait constant --- .../src/parser/parser/traits.rs | 68 +++++++++++++++++-- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 679312a7603..6ed6e677d1d 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -1,7 +1,9 @@ use noirc_errors::Span; use crate::{ - ast::{Documented, Ident, ItemVisibility, NoirTrait, TraitItem}, + ast::{ + Documented, Ident, ItemVisibility, NoirTrait, TraitItem, UnresolvedType, UnresolvedTypeData, + }, token::{Attribute, Keyword, SecondaryAttribute}, }; @@ -72,6 +74,10 @@ impl<'a> Parser<'a> { return Some(kind); } + if let Some(kind) = self.parse_trait_constant() { + return Some(kind); + } + None } @@ -80,16 +86,45 @@ impl<'a> Parser<'a> { return None; } - let name = self.eat_ident(); - if name.is_none() { - // TODO: error - } + let name = match self.eat_ident() { + Some(name) => name, + None => { + // TODO: error + Ident::default() + } + }; self.eat_semicolons(); - let name = name.unwrap_or_default(); Some(TraitItem::Type { name }) } + + 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 => { + // TODO: error + Ident::default() + } + }; + + let typ = if self.eat_colon() { + self.parse_type() + } else { + // TODO: error + UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + }; + + let default_value = if self.eat_assign() { Some(self.parse_expression()) } else { None }; + + self.eat_semicolons(); + + Some(TraitItem::Constant { name, typ, default_value }) + } } fn empty_trait( @@ -181,4 +216,25 @@ mod tests { }; assert_eq!(name.to_string(), "Elem"); } + + #[test] + fn parse_trait_with_constant() { + let src = "trait Foo { let x: Field = 1; }"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Trait(mut noir_trait) = item.kind else { + panic!("Expected trait"); + }; + 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"); + } } From 75c5fb3efb9bc8e02852ee9cff260177267a7a1e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 08:01:38 -0300 Subject: [PATCH 059/229] Parse trait functions --- .../src/parser/parser/function.rs | 79 +++++++++++----- .../src/parser/parser/traits.rs | 94 ++++++++++++++++++- 2 files changed, 145 insertions(+), 28 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 52461f24f2c..47c2d6b4d07 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -4,8 +4,8 @@ use noirc_errors::Span; use crate::{ ast::{ BlockExpression, FunctionDefinition, FunctionReturnType, GenericTypeArgs, Ident, - ItemVisibility, NoirFunction, Param, Path, Pattern, UnresolvedType, UnresolvedTypeData, - Visibility, + ItemVisibility, NoirFunction, Param, Path, Pattern, UnresolvedGenerics, + UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }, parser::ParserErrorReason, token::{Attribute, Attributes, Keyword, Token}, @@ -13,6 +13,17 @@ use crate::{ 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> { pub(crate) fn parse_function( &mut self, @@ -42,10 +53,35 @@ impl<'a> Parser<'a> { ) -> 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.push_error(ParserErrorReason::ExpectedIdentifierAfterFn, self.current_token_span); - - return empty_function(attributes, is_unconstrained, is_comptime, visibility); + return empty_function(); }; let generics = self.parse_generics(); @@ -61,15 +97,21 @@ impl<'a> Parser<'a> { let where_clause = self.parse_where_clause(); let body_start_span = self.current_token_span; - let body = self.parse_block_expression().unwrap_or_else(empty_body); - let body_span = self.span_since(body_start_span); + let (body, body_span) = if self.eat_semicolons() { + if !allow_optional_body { + // TODO: error + } - FunctionDefinition { + (None, body_start_span) + } else { + ( + Some(self.parse_block_expression().unwrap_or_else(empty_body)), + self.span_since(body_start_span), + ) + }; + + FunctionDefinitionWithOptionalBody { name, - attributes, - is_unconstrained, - is_comptime, - visibility, generics, parameters, body, @@ -230,21 +272,12 @@ impl<'a> Parser<'a> { } } -fn empty_function( - attributes: Attributes, - is_unconstrained: bool, - is_comptime: bool, - visibility: ItemVisibility, -) -> FunctionDefinition { - FunctionDefinition { +fn empty_function() -> FunctionDefinitionWithOptionalBody { + FunctionDefinitionWithOptionalBody { name: Ident::default(), - attributes, - is_unconstrained, - is_comptime, - visibility, generics: Vec::new(), parameters: Vec::new(), - body: empty_body(), + body: None, span: Span::default(), where_clause: Vec::new(), return_type: FunctionReturnType::Default(Span::default()), diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 6ed6e677d1d..2fa2aefc255 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -2,7 +2,8 @@ use noirc_errors::Span; use crate::{ ast::{ - Documented, Ident, ItemVisibility, NoirTrait, TraitItem, UnresolvedType, UnresolvedTypeData, + Documented, Ident, ItemVisibility, NoirTrait, Pattern, TraitItem, UnresolvedType, + UnresolvedTypeData, }, token::{Attribute, Keyword, SecondaryAttribute}, }; @@ -70,12 +71,16 @@ impl<'a> Parser<'a> { } fn parse_trait_item(&mut self) -> Option { - if let Some(kind) = self.parse_trait_type() { - return Some(kind); + if let Some(item) = self.parse_trait_type() { + return Some(item); } - if let Some(kind) = self.parse_trait_constant() { - return Some(kind); + if let Some(item) = self.parse_trait_constant() { + return Some(item); + } + + if let Some(item) = self.parse_trait_function() { + return Some(item); } None @@ -125,6 +130,47 @@ impl<'a> Parser<'a> { Some(TraitItem::Constant { name, typ, default_value }) } + + fn parse_trait_function(&mut self) -> Option { + let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); + let visibility = self.parse_item_visibility(); + let is_comptime = self.eat_keyword(Keyword::Comptime); + + if !self.eat_keyword(Keyword::Fn) { + // TODO: error if unconstrained, visibility or comptime + return None; + } + + let function = self.parse_function_definition_with_optional_body( + true, // allow optional body + true, // allow self + ); + + let parameters = function + .parameters + .into_iter() + .filter_map(|param| { + if let Pattern::Identifier(ident) = param.pattern { + Some((ident, param.typ)) + } else { + // TODO: error + None + } + }) + .collect(); + + Some(TraitItem::Function { + is_unconstrained, + visibility, + is_comptime, + name: function.name, + generics: function.generics, + parameters, + return_type: function.return_type, + where_clause: function.where_clause, + body: function.body, + }) + } } fn empty_trait( @@ -237,4 +283,42 @@ mod tests { assert_eq!(typ.to_string(), "Field"); assert_eq!(default_value.unwrap().to_string(), "1"); } + + #[test] + fn parse_trait_with_function_no_body() { + let src = "trait Foo { fn foo(); }"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Trait(mut noir_trait) = item.kind else { + panic!("Expected trait"); + }; + 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()); + } + + #[test] + fn parse_trait_with_function_with_body() { + let src = "trait Foo { fn foo() {} }"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Trait(mut noir_trait) = item.kind else { + panic!("Expected trait"); + }; + 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()); + } } From bcf1539bf362be2b4dca5a217990394c2287de66 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 08:14:35 -0300 Subject: [PATCH 060/229] Refactor to parse function modifiers in a single place/way --- compiler/noirc_frontend/src/parser/parser.rs | 1 + .../noirc_frontend/src/parser/parser/impls.rs | 19 ++++----- .../noirc_frontend/src/parser/parser/item.rs | 40 ++++++++++--------- .../src/parser/parser/modifiers.rs | 36 +++++++++++++++++ .../src/parser/parser/traits.rs | 14 +++---- 5 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/modifiers.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index fcddbd7f98a..0ee5d06911c 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -18,6 +18,7 @@ mod global; mod impls; mod item; mod item_visibility; +mod modifiers; mod module; mod path; mod pattern; diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 2ca8d4d2323..a8113dcaf1b 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -55,17 +55,15 @@ impl<'a> Parser<'a> { // TODO: maybe require visibility to always come first let doc_comments = self.parse_outer_doc_comments(); let start_span = self.current_token_span; - let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); - let visibility = self.parse_item_visibility(); - let is_comptime = self.eat_keyword(Keyword::Comptime); + let modifiers = self.parse_modifiers(); let attributes = Vec::new(); if self.eat_keyword(Keyword::Fn) { let method = self.parse_function( attributes, - visibility, - is_comptime, - is_unconstrained, + modifiers.visibility, + modifiers.comptime.is_some(), + modifiers.unconstrained.is_some(), true, // allow_self ); methods.push((Documented::new(method, doc_comments), self.span_since(start_span))); @@ -155,11 +153,10 @@ impl<'a> Parser<'a> { } fn parse_trait_impl_function(&mut self) -> Option { - let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); - if self.parse_item_visibility() != ItemVisibility::Private { + let modifiers = self.parse_modifiers(); + if modifiers.visibility != ItemVisibility::Private { // TODO: error } - let is_comptime = self.eat_keyword(Keyword::Comptime); let attributes = Vec::new(); if !self.eat_keyword(Keyword::Fn) { @@ -170,8 +167,8 @@ impl<'a> Parser<'a> { let noir_function = self.parse_function( attributes, ItemVisibility::Public, - is_comptime, - is_unconstrained, + modifiers.comptime.is_some(), + modifiers.unconstrained.is_some(), true, // allow_self ); Some(TraitImplItemKind::Function(noir_function)) diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index afa238a21c9..526b219345c 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -30,25 +30,15 @@ impl<'a> Parser<'a> { return Some(ItemKind::InnerAttribute(kind)); } - let attributes = self.parse_attributes(); - - // Functions allow `unconstrained` before their visibility - // so we check that before and after the visibility - let mut unconstrained = self.eat_keyword(Keyword::Unconstrained); - - let visibility = self.parse_item_visibility(); - let start_span = self.current_token_span; + let attributes = self.parse_attributes(); - unconstrained = unconstrained || self.eat_keyword(Keyword::Unconstrained); - - let comptime = self.eat_keyword(Keyword::Comptime); - let mutable = self.eat_keyword(Keyword::Mut); + let modifiers = self.parse_modifiers(); if self.eat_keyword(Keyword::Use) { // TODO: error if there's comptime, mutable or unconstrained let use_tree = self.parse_use_tree(); - return Some(ItemKind::Import(use_tree, visibility)); + return Some(ItemKind::Import(use_tree, modifiers.visibility)); } if let Some(is_contract) = self.eat_mod_or_contract() { @@ -58,7 +48,11 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Struct) { // TODO: error if there's comptime or mutable or unconstrained - return Some(ItemKind::Struct(self.parse_struct(attributes, visibility, start_span))); + return Some(ItemKind::Struct(self.parse_struct( + attributes, + modifiers.visibility, + start_span, + ))); } if self.eat_keyword(Keyword::Impl) { @@ -71,12 +65,20 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Trait) { // TODO: error if there's comptime or mutable or unconstrained - return Some(ItemKind::Trait(self.parse_trait(attributes, visibility, start_span))); + return Some(ItemKind::Trait(self.parse_trait( + attributes, + modifiers.visibility, + start_span, + ))); } if self.eat_keyword(Keyword::Global) { // TODO: error if there's unconstrained - return Some(ItemKind::Global(self.parse_global(attributes, comptime, mutable))); + return Some(ItemKind::Global(self.parse_global( + attributes, + modifiers.comptime.is_some(), + modifiers.mutable.is_some(), + ))); } if self.eat_keyword(Keyword::Type) { @@ -88,9 +90,9 @@ impl<'a> Parser<'a> { // TODO: error if there's mutable return Some(ItemKind::Function(self.parse_function( attributes, - visibility, - comptime, - unconstrained, + modifiers.visibility, + modifiers.comptime.is_some(), + modifiers.unconstrained.is_some(), false, // allow_self ))); } 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..6975f3e5afb --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -0,0 +1,36 @@ +use noirc_errors::Span; + +use crate::{ast::ItemVisibility, token::Keyword}; + +use super::Parser; + +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> { + pub(crate) fn parse_modifiers(&mut self) -> Modifiers { + let unconstrained = self.parse_modifier(Keyword::Unconstrained); + + let start_span = self.current_token_span; + let visibility = self.parse_item_visibility(); + let visibility_span = self.span_since(start_span); + + let comptime = self.parse_modifier(Keyword::Comptime); + let mutable = self.parse_modifier(Keyword::Mut); + + Modifiers { visibility, visibility_span, unconstrained, comptime, mutable } + } + + fn parse_modifier(&mut self, keyword: Keyword) -> Option { + if self.eat_keyword(keyword) { + Some(self.previous_token_span) + } else { + None + } + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 2fa2aefc255..c1d193e5f12 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -132,9 +132,7 @@ impl<'a> Parser<'a> { } fn parse_trait_function(&mut self) -> Option { - let is_unconstrained = self.eat_keyword(Keyword::Unconstrained); - let visibility = self.parse_item_visibility(); - let is_comptime = self.eat_keyword(Keyword::Comptime); + let modifiers = self.parse_modifiers(); if !self.eat_keyword(Keyword::Fn) { // TODO: error if unconstrained, visibility or comptime @@ -160,9 +158,9 @@ impl<'a> Parser<'a> { .collect(); Some(TraitItem::Function { - is_unconstrained, - visibility, - is_comptime, + is_unconstrained: modifiers.unconstrained.is_some(), + visibility: modifiers.visibility, + is_comptime: modifiers.comptime.is_some(), name: function.name, generics: function.generics, parameters, @@ -178,7 +176,7 @@ fn empty_trait( visibility: ItemVisibility, span: Span, ) -> NoirTrait { - return NoirTrait { + NoirTrait { name: Ident::default(), generics: Vec::new(), where_clause: Vec::new(), @@ -186,7 +184,7 @@ fn empty_trait( items: Vec::new(), attributes, visibility, - }; + } } #[cfg(test)] From 19e5cd5198673e8f51736e737cf06958faf5a312 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 08:20:48 -0300 Subject: [PATCH 061/229] Parse trait impl constants --- .../noirc_frontend/src/parser/parser/impls.rs | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index a8113dcaf1b..d7222a6b348 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -2,9 +2,9 @@ use noirc_errors::Span; use crate::{ ast::{ - Documented, GenericTypeArgs, Ident, ItemVisibility, NoirFunction, NoirTraitImpl, Path, - TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedType, - UnresolvedTypeData, + Documented, Expression, ExpressionKind, GenericTypeArgs, Ident, ItemVisibility, + NoirFunction, NoirTraitImpl, Path, TraitImplItem, TraitImplItemKind, TypeImpl, + UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, token::Keyword, }; @@ -149,6 +149,10 @@ impl<'a> Parser<'a> { return Some(kind); } + if let Some(kind) = self.parse_trait_impl_constant() { + return Some(kind); + } + None } @@ -198,6 +202,37 @@ impl<'a> Parser<'a> { Some(TraitImplItemKind::Type { name, alias }) } + + 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 => { + // TODO: error + Ident::default() + } + }; + + let typ = if self.eat_colon() { + self.parse_type() + } else { + UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + }; + + let expr = if self.eat_assign() { + self.parse_expression() + } else { + // TODO: error + Expression { kind: ExpressionKind::Error, span: Span::default() } + }; + + self.eat_semicolons(); + + Some(TraitImplItemKind::Constant(name, typ, expr)) + } } #[cfg(test)] @@ -456,4 +491,26 @@ mod tests { 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::TraitImpl(mut trait_impl) = item.kind else { + panic!("Expected trait impl"); + }; + 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"); + } } From aa0a058cd78370580ac6f75aa0eaf6a0174ac625 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 09:51:39 -0300 Subject: [PATCH 062/229] Fix some spans --- .../noirc_frontend/src/parser/parser/function.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 47c2d6b4d07..af39e661a9a 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -81,7 +81,7 @@ impl<'a> Parser<'a> { ) -> FunctionDefinitionWithOptionalBody { let Some(name) = self.eat_ident() else { self.push_error(ParserErrorReason::ExpectedIdentifierAfterFn, self.current_token_span); - return empty_function(); + return empty_function(self.previous_token_span); }; let generics = self.parse_generics(); @@ -91,7 +91,12 @@ impl<'a> Parser<'a> { let visibility = self.parse_visibility(); (FunctionReturnType::Ty(self.parse_type()), visibility) } else { - (FunctionReturnType::Default(Span::default()), Visibility::Private) + ( + FunctionReturnType::Default(Span::from( + self.previous_token_span.end()..self.previous_token_span.end(), + )), + Visibility::Private, + ) }; let where_clause = self.parse_where_clause(); @@ -102,7 +107,7 @@ impl<'a> Parser<'a> { // TODO: error } - (None, body_start_span) + (None, Span::from(body_start_span.end()..body_start_span.end())) } else { ( Some(self.parse_block_expression().unwrap_or_else(empty_body)), @@ -272,13 +277,13 @@ impl<'a> Parser<'a> { } } -fn empty_function() -> FunctionDefinitionWithOptionalBody { +fn empty_function(span: Span) -> FunctionDefinitionWithOptionalBody { FunctionDefinitionWithOptionalBody { name: Ident::default(), generics: Vec::new(), parameters: Vec::new(), body: None, - span: Span::default(), + span: Span::from(span.end()..span.end()), where_clause: Vec::new(), return_type: FunctionReturnType::Default(Span::default()), return_visibility: Visibility::Private, From 4236b2e41eeb915f6880600a22fd8ff7de512db2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 10:04:43 -0300 Subject: [PATCH 063/229] Always check errors --- .../src/parser/parser/attributes.rs | 9 +++-- .../src/parser/parser/doc_comments.rs | 8 +++- .../src/parser/parser/expression.rs | 20 +++++++--- .../src/parser/parser/generics.rs | 16 ++++++-- .../src/parser/parser/item_visibility.rs | 12 ++++-- .../noirc_frontend/src/parser/parser/path.rs | 24 +++++++++--- .../src/parser/parser/pattern.rs | 21 +++++++--- .../noirc_frontend/src/parser/parser/types.rs | 38 ++++++++++++++----- .../src/parser/parser/where_clause.rs | 12 ++++-- 9 files changed, 119 insertions(+), 41 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 112f84edd3f..4008eaa6d25 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -57,17 +57,20 @@ mod tests { #[test] fn parses_inner_attribute() { let src = "#![hello]"; - let Some(SecondaryAttribute::Custom(custom)) = Parser::for_str(src).parse_inner_attribute() - else { + let mut parser = Parser::for_str(src); + let Some(SecondaryAttribute::Custom(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner custom attribute"); }; + assert!(parser.errors.is_empty()); assert_eq!(custom.contents, "hello"); } #[test] fn parses_attributes() { let src = "#[test] #[deprecated]"; - let mut attributes = Parser::for_str(src).parse_attributes(); + let mut parser = Parser::for_str(src); + let mut attributes = parser.parse_attributes(); + assert!(parser.errors.is_empty()); assert_eq!(attributes.len(), 2); let (attr, _) = attributes.remove(0); diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 3c46e881a42..0142e00d0a6 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -43,7 +43,9 @@ mod tests { #[test] fn parses_inner_doc_comments() { let src = "//! Hello\n//! World"; - let comments = Parser::for_str(src).parse_inner_doc_comments(); + let mut parser = Parser::for_str(src); + let comments = parser.parse_inner_doc_comments(); + assert!(parser.errors.is_empty()); assert_eq!(comments.len(), 2); assert_eq!(comments[0], " Hello"); assert_eq!(comments[1], " World"); @@ -52,7 +54,9 @@ mod tests { #[test] fn parses_outer_doc_comments() { let src = "/// Hello\n/// World"; - let comments = Parser::for_str(src).parse_outer_doc_comments(); + let mut parser = Parser::for_str(src); + let comments = parser.parse_outer_doc_comments(); + assert!(parser.errors.is_empty()); 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 index 1007b49b5fc..a604d7c47cb 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -107,7 +107,9 @@ mod tests { #[test] fn parses_bool_literals() { let src = "true"; - let expr = Parser::for_str(src).parse_expression(); + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(true)))); let src = "false"; @@ -118,7 +120,9 @@ mod tests { #[test] fn parses_integer_literal() { let src = "42"; - let expr = Parser::for_str(src).parse_expression(); + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); }; @@ -129,7 +133,9 @@ mod tests { #[test] fn parses_parenthesized_expression() { let src = "(42)"; - let expr = Parser::for_str(src).parse_expression(); + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); }; @@ -150,7 +156,9 @@ mod tests { #[test] fn parses_tuple_expression() { let src = "(1, 2)"; - let expr = Parser::for_str(src).parse_expression(); + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); let ExpressionKind::Tuple(mut exprs) = expr.kind else { panic!("Expected tuple expression"); }; @@ -174,7 +182,9 @@ mod tests { #[test] fn parses_block_expression_with_a_single_expression() { let src = "{ 1 }"; - let expr = Parser::for_str(src).parse_expression(); + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); let ExpressionKind::Block(mut block) = expr.kind else { panic!("Expected block expression"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index d0da27857b6..cb6a654cc9a 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -155,7 +155,9 @@ mod tests { #[test] fn parses_generics() { let src = ""; - let mut generics = Parser::for_str(src).parse_generics(); + let mut parser = Parser::for_str(src); + let mut generics = parser.parse_generics(); + assert!(parser.errors.is_empty()); assert_eq!(generics.len(), 2); let generic = generics.remove(0); @@ -178,14 +180,18 @@ mod tests { #[test] fn parses_no_generic_type_args() { let src = "1"; - let generics = Parser::for_str(src).parse_generic_type_args(); + let mut parser = Parser::for_str(src); + let generics = parser.parse_generic_type_args(); + assert!(parser.errors.is_empty()); assert!(generics.is_empty()); } #[test] fn parses_generic_type_args() { let src = ""; - let generics = Parser::for_str(src).parse_generic_type_args(); + let mut parser = Parser::for_str(src); + let generics = parser.parse_generic_type_args(); + assert!(parser.errors.is_empty()); assert!(!generics.is_empty()); assert_eq!(generics.ordered_args.len(), 1); assert_eq!(generics.ordered_args[0].to_string(), "i32"); @@ -197,7 +203,9 @@ mod tests { #[test] fn parses_generic_type_arg_that_is_a_path() { let src = ""; - let generics = Parser::for_str(src).parse_generic_type_args(); + let mut parser = Parser::for_str(src); + let generics = parser.parse_generic_type_args(); + assert!(parser.errors.is_empty()); assert!(!generics.is_empty()); assert_eq!(generics.ordered_args.len(), 1); assert_eq!(generics.ordered_args[0].to_string(), "foo::Bar"); diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index af21fae8830..64d6f848006 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -37,21 +37,27 @@ mod tests { #[test] fn parses_private_visibility() { let src = "("; - let visibility = Parser::for_str(src).parse_item_visibility(); + let mut parser = Parser::for_str(src); + let visibility = parser.parse_item_visibility(); + assert!(parser.errors.is_empty()); assert_eq!(visibility, ItemVisibility::Private); } #[test] fn parses_public_visibility() { let src = "pub"; - let visibility = Parser::for_str(src).parse_item_visibility(); + let mut parser = Parser::for_str(src); + let visibility = parser.parse_item_visibility(); + assert!(parser.errors.is_empty()); assert_eq!(visibility, ItemVisibility::Public); } #[test] fn parses_public_crate_visibility() { let src = "pub(crate)"; - let visibility = Parser::for_str(src).parse_item_visibility(); + let mut parser = Parser::for_str(src); + let visibility = parser.parse_item_visibility(); + assert!(parser.errors.is_empty()); assert_eq!(visibility, ItemVisibility::PublicCrate); } } diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 81afd68c213..27b69ebeff0 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -138,7 +138,9 @@ mod tests { #[test] fn parses_plain_one_segment() { let src = "foo"; - let path = Parser::for_str(src).parse_path(); + let mut parser = Parser::for_str(src); + let path = parser.parse_path(); + assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 1); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -148,7 +150,9 @@ mod tests { #[test] fn parses_plain_two_segments() { let src = "foo::bar"; - let path = Parser::for_str(src).parse_path(); + let mut parser = Parser::for_str(src); + let path = parser.parse_path(); + assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -160,7 +164,9 @@ mod tests { #[test] fn parses_crate_two_segments() { let src = "crate::foo::bar"; - let path = Parser::for_str(src).parse_path(); + let mut parser = Parser::for_str(src); + let path = parser.parse_path(); + assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Crate); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -172,7 +178,9 @@ mod tests { #[test] fn parses_super_two_segments() { let src = "super::foo::bar"; - let path = Parser::for_str(src).parse_path(); + let mut parser = Parser::for_str(src); + let path = parser.parse_path(); + assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Super); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -184,7 +192,9 @@ mod tests { #[test] fn parses_dep_two_segments() { let src = "dep::foo::bar"; - let path = Parser::for_str(src).parse_path(); + let mut parser = Parser::for_str(src); + let path = parser.parse_path(); + assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Dep); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -209,7 +219,9 @@ mod tests { #[test] fn parses_with_turbofish() { let src = "foo::::bar"; - let mut path = Parser::for_str(src).parse_path(); + let mut parser = Parser::for_str(src); + let mut path = parser.parse_path(); + assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index ece098c7dfe..6fd48c35bf0 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -168,7 +168,9 @@ mod tests { #[test] fn parses_identifier_pattern() { let src = "foo"; - let typ = Parser::for_str(src).parse_pattern(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_pattern(); + assert!(parser.errors.is_empty()); let Pattern::Identifier(ident) = typ else { panic!("Expected an identifier pattern") }; assert_eq!(ident.to_string(), "foo"); } @@ -176,7 +178,9 @@ mod tests { #[test] fn parses_mutable_pattern() { let src = "mut foo"; - let typ = Parser::for_str(src).parse_pattern(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_pattern(); + assert!(parser.errors.is_empty()); let Pattern::Mutable(pattern, _, _) = typ else { panic!("Expected a mutable pattern") }; let pattern: &Pattern = &pattern; let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; @@ -186,7 +190,9 @@ mod tests { #[test] fn parses_tuple_pattern() { let src = "(foo, bar)"; - let typ = Parser::for_str(src).parse_pattern(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_pattern(); + assert!(parser.errors.is_empty()); let Pattern::Tuple(mut patterns, _) = typ else { panic!("Expected a tuple pattern") }; assert_eq!(patterns.len(), 2); @@ -212,7 +218,9 @@ mod tests { #[test] fn parses_struct_pattern_no_fields() { let src = "foo::Bar {}"; - let typ = Parser::for_str(src).parse_pattern(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_pattern(); + assert!(parser.errors.is_empty()); let Pattern::Struct(path, patterns, _) = typ else { panic!("Expected a struct pattern") }; assert_eq!(path.to_string(), "foo::Bar"); assert!(patterns.is_empty()); @@ -221,7 +229,9 @@ mod tests { #[test] fn parses_struct_pattern() { let src = "foo::Bar { x: one, y }"; - let typ = Parser::for_str(src).parse_pattern(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_pattern(); + assert!(parser.errors.is_empty()); let Pattern::Struct(path, mut patterns, _) = typ else { panic!("Expected a struct pattern") }; @@ -242,6 +252,7 @@ mod tests { let src = "foo::Bar { x"; let mut parser = Parser::for_str(src); let typ = parser.parse_pattern(); + assert!(parser.errors.is_empty()); let Pattern::Struct(path, _, _) = typ else { panic!("Expected a struct pattern") }; assert_eq!(path.to_string(), "foo::Bar"); } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index dfc7b9c47a0..dc4b6fe4cae 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -154,21 +154,27 @@ mod tests { #[test] fn parses_unit_type() { let src = "()"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); assert!(matches!(typ.typ, UnresolvedTypeData::Unit)); } #[test] fn parses_bool_type() { let src = "bool"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); } #[test] fn parses_int_type() { let src = "u32"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); assert!(matches!( typ.typ, UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) @@ -178,14 +184,18 @@ mod tests { #[test] fn parses_field_type() { let src = "Field"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } #[test] fn parses_tuple_type() { let src = "(Field, bool)"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; assert_eq!(types.len(), 2); @@ -199,7 +209,9 @@ mod tests { #[test] fn parses_tuple_type_one_element() { let src = "(Field,)"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; assert_eq!(types.len(), 1); @@ -210,7 +222,9 @@ mod tests { #[test] fn parses_parenthesized_type() { let src = "(Field)"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; @@ -221,8 +235,8 @@ mod tests { fn parses_unclosed_parentheses_type() { let src = "(Field"; let mut parser = Parser::for_str(src); - assert!(parser.errors.is_empty()); // TODO: there should be an error here let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); // TODO: there should be an error here let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; @@ -232,7 +246,9 @@ mod tests { #[test] fn parses_mutable_reference_type() { let src = "&mut Field"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); let UnresolvedTypeData::MutableReference(typ) = typ.typ else { panic!("Expected a mutable reference type") }; @@ -242,7 +258,9 @@ mod tests { #[test] fn parses_named_type_no_generics() { let src = "foo::Bar"; - let typ = Parser::for_str(src).parse_type(); + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { panic!("Expected a named type") }; diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 4838396eb94..19ebc031ab1 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -82,14 +82,18 @@ mod tests { #[test] fn parses_no_where_clause() { let src = "{"; - let constraints = Parser::for_str(src).parse_where_clause(); + let mut parser = Parser::for_str(src); + let constraints = parser.parse_where_clause(); + assert!(parser.errors.is_empty()); assert!(constraints.is_empty()); } #[test] fn parses_one_where_clause_with_two_constraints() { let src = "where Foo: Bar + Baz"; - let mut constraints = Parser::for_str(src).parse_where_clause(); + let mut parser = Parser::for_str(src); + let mut constraints = parser.parse_where_clause(); + assert!(parser.errors.is_empty()); assert_eq!(constraints.len(), 2); let constraint = constraints.remove(0); @@ -105,7 +109,9 @@ mod tests { #[test] fn parses_two_where_clauses() { let src = "where Foo: Bar, i32: Qux"; - let mut constraints = Parser::for_str(src).parse_where_clause(); + let mut parser = Parser::for_str(src); + let mut constraints = parser.parse_where_clause(); + assert!(parser.errors.is_empty()); assert_eq!(constraints.len(), 2); let constraint = constraints.remove(0); From 2a177fefd672d630eb687b51db9029da92048b5b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 10:47:21 -0300 Subject: [PATCH 064/229] Some helpers to test errors and their spans --- compiler/noirc_frontend/src/parser/parser.rs | 1 + .../src/parser/parser/expression.rs | 37 +++++++++++-------- .../src/parser/parser/function.rs | 20 +++++++--- .../src/parser/parser/structs.rs | 20 +++++++--- .../noirc_frontend/src/parser/parser/tests.rs | 30 +++++++++++++++ 5 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/tests.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 0ee5d06911c..53c7b67b9b3 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -23,6 +23,7 @@ mod module; mod path; mod pattern; mod structs; +mod tests; mod traits; mod type_alias; mod types; diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a604d7c47cb..6dc224328c3 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -101,7 +101,10 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ExpressionKind, Literal, StatementKind}, - parser::{Parser, ParserErrorReason}, + parser::{ + parser::tests::{get_single_error, get_source_with_error_span}, + Parser, ParserErrorReason, + }, }; #[test] @@ -204,23 +207,27 @@ mod tests { #[test] fn parses_unclosed_parentheses() { - let src = "("; - let mut parser = Parser::for_str(src); - let _ = parser.parse_expression(); - assert_eq!(parser.errors.len(), 1); - - let error = parser.errors[0].reason().unwrap(); - assert!(matches!(error, ParserErrorReason::ExpectedExpression)); + let src = " + ( + ^ + "; + let (src, span) = get_source_with_error_span(src); + let mut parser = Parser::for_str(&src); + parser.parse_expression(); + let reason = get_single_error(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedExpression)); } #[test] fn parses_missing_comma() { - let src = "(1 2)"; - let mut parser = Parser::for_str(src); - let _ = parser.parse_expression(); - assert_eq!(parser.errors.len(), 1); - - let error = parser.errors[0].reason().unwrap(); - assert!(matches!(error, ParserErrorReason::MissingCommaSeparatingExpressions)); + 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(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::MissingCommaSeparatingExpressions)); } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index af39e661a9a..716b9ffd696 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -298,7 +298,13 @@ fn empty_body() -> BlockExpression { mod tests { use crate::{ ast::{UnresolvedTypeData, Visibility}, - parser::{parser::parse_program, ItemKind, ParserErrorReason}, + parser::{ + parser::{ + parse_program, + tests::{get_single_error, get_source_with_error_span}, + }, + ItemKind, ParserErrorReason, + }, }; #[test] @@ -445,11 +451,13 @@ mod tests { #[test] fn parse_error_multiple_function_attributes_found() { - let src = "#[foreign(foo)] #[oracle(bar)] fn foo() {}"; - let (_, errors) = parse_program(src); - assert_eq!(errors.len(), 1); - - let reason = errors[0].reason().unwrap(); + 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(&errors, span); assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); } } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index f38b13daecd..45a8faa1f8c 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -93,7 +93,13 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, - parser::{parser::parse_program, ItemKind, ParserErrorReason}, + parser::{ + parser::{ + parse_program, + tests::{get_single_error, get_source_with_error_span}, + }, + ItemKind, ParserErrorReason, + }, }; #[test] @@ -196,11 +202,13 @@ mod tests { #[test] fn parse_error_no_function_attributes_allowed_on_struct() { - let src = "#[test] struct Foo {}"; - let (_, errors) = parse_program(src); - assert_eq!(errors.len(), 1); - - let reason = errors[0].reason().unwrap(); + let src = " + #[test] struct Foo {} + ^^^^^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (_, errors) = parse_program(&src); + let reason = get_single_error(&errors, span); assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnStruct)); } } 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..d9551b914a7 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/tests.rs @@ -0,0 +1,30 @@ +#![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<'a>( + errors: &[ParserError], + expected_span: Span, +) -> &ParserErrorReason { + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].span(), expected_span); + &errors[0].reason().unwrap() +} From 0607a9f9076d431832575fa4390339174e417ad1 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 10:54:02 -0300 Subject: [PATCH 065/229] Errors for item visibility --- compiler/noirc_frontend/src/parser/errors.rs | 4 ++ .../src/parser/parser/item_visibility.rs | 58 +++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index ac7ba44d997..d748ff3a172 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -43,6 +43,10 @@ pub enum ParserErrorReason { ExpectedLeftParen, #[error("Expected an integer")] ExpectedInteger, + #[error("Expected `crate` after `pub`")] + ExpectedCrateAfterPub, + #[error("Expected `)` after `pub(crate`")] + ExpectedParenAfterPubCrate, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 64d6f848006..0670f2f9a81 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -1,4 +1,4 @@ -use crate::{ast::ItemVisibility, token::Keyword}; +use crate::{ast::ItemVisibility, parser::ParserErrorReason, token::Keyword}; use super::Parser; @@ -14,15 +14,18 @@ impl<'a> Parser<'a> { } if !self.eat_keyword(Keyword::Crate) { - // TODO: error // `pub(` or `pub()` + self.push_error(ParserErrorReason::ExpectedCrateAfterPub, self.current_token_span); self.eat_right_paren(); return ItemVisibility::Public; } if !self.eat_right_paren() { // `pub(crate` - // TODO: error + self.push_error( + ParserErrorReason::ExpectedParenAfterPubCrate, + self.previous_token_span, + ); } // `pub(crate)`` @@ -32,7 +35,13 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::{ast::ItemVisibility, parser::Parser}; + use crate::{ + ast::ItemVisibility, + parser::{ + parser::tests::{get_single_error, get_source_with_error_span}, + Parser, ParserErrorReason, + }, + }; #[test] fn parses_private_visibility() { @@ -52,6 +61,47 @@ mod tests { 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 reason = get_single_error(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedCrateAfterPub)); + } + + #[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 reason = get_single_error(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedCrateAfterPub)); + } + #[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 reason = get_single_error(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedParenAfterPubCrate)); + } + #[test] fn parses_public_crate_visibility() { let src = "pub(crate)"; From 9084e7cc0d1eb8fbadb48d0a402e905549e320ec Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 11:48:51 -0300 Subject: [PATCH 066/229] Some errors --- compiler/noirc_frontend/src/parser/errors.rs | 6 +++ .../src/parser/parser/expression.rs | 2 +- .../src/parser/parser/function.rs | 14 +++++- .../src/parser/parser/generics.rs | 44 ++++++++++++++++-- .../src/parser/parser/global.rs | 45 +++++++++++++++++-- 5 files changed, 102 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index d748ff3a172..c6977805f90 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -47,6 +47,12 @@ pub enum ParserErrorReason { ExpectedCrateAfterPub, #[error("Expected `)` after `pub(crate`")] ExpectedParenAfterPubCrate, + #[error("Expected a function body (`{{ ... }}`), not `;`")] + ExpectedFunctionBody, + #[error("Expected the global to have a value")] + GlobalWithoutValue, + #[error("Expected `;` after a global declaration")] + ExpectedSemicolonAfterGlobal, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 6dc224328c3..549a6ebfcec 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -90,7 +90,7 @@ impl<'a> Parser<'a> { statements.push(Statement { kind: StatementKind::Expression(expr), span }); if !self.eat_right_brace() { - // TODO: error + // TODO: error (for later) } Some(BlockExpression { statements }) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 716b9ffd696..b8ada4114d0 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -104,7 +104,7 @@ impl<'a> Parser<'a> { let body_start_span = self.current_token_span; let (body, body_span) = if self.eat_semicolons() { if !allow_optional_body { - // TODO: error + self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_span); } (None, Span::from(body_start_span.end()..body_start_span.end())) @@ -460,4 +460,16 @@ mod tests { let reason = get_single_error(&errors, span); assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); } + + #[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(&errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedFunctionBody)); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index cb6a654cc9a..ac7f38093ac 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -20,7 +20,7 @@ impl<'a> Parser<'a> { } if self.eat_greater() { - // TODO: error? + // TODO: error? (for later) return generics; } @@ -69,8 +69,15 @@ impl<'a> Parser<'a> { } let typ = self.parse_type(); - - // TODO: error if typ isn't an integer type + 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); + } + } else { + self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); + } return Some(UnresolvedGeneric::Numeric { ident, typ }); } @@ -142,7 +149,10 @@ fn type_u32() -> UnresolvedType { mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, - parser::Parser, + parser::{ + parser::tests::{get_single_error, get_source_with_error_span}, + Parser, ParserErrorReason, + }, }; #[test] @@ -211,4 +221,30 @@ mod tests { assert_eq!(generics.ordered_args[0].to_string(), "foo::Bar"); assert_eq!(generics.named_args.len(), 0); } + + #[test] + fn parse_numeric_generic_error_if_not_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(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); + } + + #[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(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index 0f31eeee6fc..15b0a126034 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -5,6 +5,7 @@ use crate::{ Expression, ExpressionKind, Ident, LetStatement, Pattern, UnresolvedType, UnresolvedTypeData, }, + parser::ParserErrorReason, token::Attribute, }; @@ -17,7 +18,8 @@ impl<'a> Parser<'a> { comptime: bool, mutable: bool, ) -> LetStatement { - // TODO: error if mutable but not comptime + // 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); @@ -42,10 +44,17 @@ impl<'a> Parser<'a> { let expression = if self.eat_assign() { self.parse_expression() } else { - // TODO: error + self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.span()); Expression { kind: ExpressionKind::Error, span: Span::default() } }; + if !self.eat_semicolon() { + self.push_error( + ParserErrorReason::ExpectedSemicolonAfterGlobal, + self.current_token_span, + ); + } + LetStatement { pattern, r#type: typ, expression, attributes, comptime } } } @@ -62,7 +71,13 @@ fn ident_to_pattern(ident: Ident, mutable: bool) -> Pattern { mod tests { use crate::{ ast::{IntegerBitSize, Pattern, Signedness, UnresolvedTypeData}, - parser::{parser::parse_program, ItemKind}, + parser::{ + parser::{ + parse_program, + tests::{get_single_error, get_source_with_error_span}, + }, + ItemKind, ParserErrorReason, + }, }; #[test] @@ -135,4 +150,28 @@ mod tests { }; 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(&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 reason = get_single_error(&errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedSemicolonAfterGlobal)); + } } From 4f02169d92e8ac75261d941ff60c52f9b6e5519d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 12:20:25 -0300 Subject: [PATCH 067/229] Parse slice and array type --- compiler/noirc_frontend/src/parser/errors.rs | 4 + compiler/noirc_frontend/src/parser/parser.rs | 9 +++ .../src/parser/parser/type_expression.rs | 18 +++++ .../noirc_frontend/src/parser/parser/types.rs | 75 ++++++++++++++++++- 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/type_expression.rs diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index c6977805f90..f3112013c91 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -53,6 +53,10 @@ pub enum ParserErrorReason { GlobalWithoutValue, #[error("Expected `;` after a global declaration")] ExpectedSemicolonAfterGlobal, + #[error("Expected `]` after slice type")] + ExpectedBracketAfterSliceType, + #[error("Expected `]` after array type")] + ExpectedBracketAfterArrayType, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 53c7b67b9b3..34e0644d0fd 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -26,6 +26,7 @@ mod structs; mod tests; mod traits; mod type_alias; +mod type_expression; mod types; mod use_tree; mod where_clause; @@ -280,6 +281,14 @@ impl<'a> Parser<'a> { self.eat(Token::RightBrace) } + fn eat_left_bracket(&mut self) -> bool { + self.eat(Token::LeftBracket) + } + + fn eat_right_bracket(&mut self) -> bool { + self.eat(Token::RightBracket) + } + fn eat_less(&mut self) -> bool { self.eat(Token::Less) } 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..8a70c00b16d --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -0,0 +1,18 @@ +use crate::ast::UnresolvedTypeExpression; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_type_expression(&mut self) -> Option { + let expr = self.parse_expression(); + let span = expr.span; + let type_expr = UnresolvedTypeExpression::from_expr(expr, span); + match type_expr { + Ok(type_expr) => Some(type_expr), + Err(parser_error) => { + self.errors.push(parser_error); + None + } + } + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index dc4b6fe4cae..d3916ecf906 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -2,6 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, UnresolvedType, UnresolvedTypeData}, + parser::ParserErrorReason, token::{Keyword, Token}, }; @@ -34,6 +35,10 @@ impl<'a> Parser<'a> { return typ; } + if let Some(typ) = self.parse_array_or_slice_type() { + return typ; + } + if let Some(typ) = self.parses_mutable_reference_type() { return typ; } @@ -90,6 +95,35 @@ impl<'a> Parser<'a> { None } + fn parse_array_or_slice_type(&mut self) -> Option { + if !self.eat_left_bracket() { + return None; + } + + let typ = self.parse_type(); + + if self.eat_semicolon() { + if let Some(expr) = self.parse_type_expression() { + if !self.eat_right_bracket() { + self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); + } + Some(UnresolvedTypeData::Array(expr, Box::new(typ))) + } else { + if !self.eat_right_bracket() { + self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); + } + + Some(UnresolvedTypeData::Slice(Box::new(typ))) + } + } else { + if !self.eat_right_bracket() { + self.push_error(ParserErrorReason::ExpectedBracketAfterSliceType, typ.span); + } + + Some(UnresolvedTypeData::Slice(Box::new(typ))) + } + } + fn parse_parentheses_type(&mut self) -> Option { if !self.eat_left_paren() { return None; @@ -148,7 +182,10 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, - parser::Parser, + parser::{ + parser::tests::{get_single_error, get_source_with_error_span}, + Parser, ParserErrorReason, + }, }; #[test] @@ -267,4 +304,40 @@ mod tests { assert_eq!(path.to_string(), "foo::Bar"); assert!(generics.is_empty()); } + + #[test] + fn parses_slice_type() { + let src = "[Field]"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); + 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 reason = get_single_error(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedBracketAfterSliceType)); + } + + #[test] + fn parses_array_type() { + let src = "[Field; 10]"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); + 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"); + } } From b65d53e1f6342388f734f68a468ed47b9ab48a92 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 12:54:45 -0300 Subject: [PATCH 068/229] Parse string literals --- compiler/noirc_frontend/src/parser/parser.rs | 39 +++++++++++++ .../src/parser/parser/expression.rs | 55 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 34e0644d0fd..c277e6e8488 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -227,6 +227,45 @@ impl<'a> Parser<'a> { } } + fn eat_str(&mut self) -> Option { + if matches!(self.token.token(), Token::Str(..)) { + let token = std::mem::take(&mut self.token); + self.next_token(); + match token.into_token() { + Token::Str(string) => Some(string), + _ => unreachable!(), + } + } else { + None + } + } + + fn eat_raw_str(&mut self) -> Option<(String, u8)> { + if matches!(self.token.token(), Token::RawStr(..)) { + let token = std::mem::take(&mut self.token); + self.next_token(); + match token.into_token() { + Token::RawStr(string, n) => Some((string, n)), + _ => unreachable!(), + } + } else { + None + } + } + + fn eat_fmt_str(&mut self) -> Option { + if matches!(self.token.token(), Token::FmtStr(..)) { + let token = std::mem::take(&mut self.token); + self.next_token(); + match token.into_token() { + Token::FmtStr(string) => Some(string), + _ => unreachable!(), + } + } else { + None + } + } + fn eat_comma(&mut self) -> bool { self.eat(Token::Comma) } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 549a6ebfcec..844490e8220 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -23,6 +23,18 @@ impl<'a> Parser<'a> { return ExpressionKind::integer(int); } + if let Some(string) = self.eat_str() { + return ExpressionKind::Literal(Literal::Str(string)); + } + + if let Some((string, n)) = self.eat_raw_str() { + return ExpressionKind::Literal(Literal::RawStr(string, n)); + } + + if let Some(string) = self.eat_fmt_str() { + return ExpressionKind::Literal(Literal::FmtStr(string)); + } + if let Some(kind) = self.parse_parentheses_expression() { return kind; } @@ -152,10 +164,49 @@ mod tests { #[test] fn parses_unit() { let src = "()"; - let expr = Parser::for_str(src).parse_expression(); + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Unit))); } + #[test] + fn parses_str() { + let src = "\"hello\""; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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)"; @@ -219,7 +270,7 @@ mod tests { } #[test] - fn parses_missing_comma() { + fn parses_missing_comma_in_tuple() { let src = " (1 2) ^ From f20bafb969ee3b7fa16cbc031b292bbb698e2257 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 12:56:28 -0300 Subject: [PATCH 069/229] Handle one more error --- compiler/noirc_frontend/src/parser/parser/impls.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index d7222a6b348..3f8ec2662e3 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -6,6 +6,7 @@ use crate::{ NoirFunction, NoirTraitImpl, Path, TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, + parser::ParserErrorReason, token::Keyword, }; @@ -159,7 +160,10 @@ impl<'a> Parser<'a> { fn parse_trait_impl_function(&mut self) -> Option { let modifiers = self.parse_modifiers(); if modifiers.visibility != ItemVisibility::Private { - // TODO: error + self.push_error( + ParserErrorReason::TraitImplVisibilityIgnored, + modifiers.visibility_span, + ); } let attributes = Vec::new(); From 913f62f5ac8fde6472f56caa6d11a05db2d8f21b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 13:12:32 -0300 Subject: [PATCH 070/229] Parse array literals --- compiler/noirc_frontend/src/parser/errors.rs | 2 + .../src/parser/parser/expression.rs | 132 +++++++++++++++++- 2 files changed, 130 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index f3112013c91..08a6cbe825a 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -57,6 +57,8 @@ pub enum ParserErrorReason { ExpectedBracketAfterSliceType, #[error("Expected `]` after array type")] ExpectedBracketAfterArrayType, + #[error("Expected `]` after array")] + ExpectedBracketAfterArray, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 844490e8220..11fab859b49 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,5 +1,8 @@ use crate::{ - ast::{BlockExpression, Expression, ExpressionKind, Literal, Statement, StatementKind}, + ast::{ + ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, Statement, + StatementKind, + }, parser::ParserErrorReason, }; @@ -35,6 +38,10 @@ impl<'a> Parser<'a> { return ExpressionKind::Literal(Literal::FmtStr(string)); } + if let Some(kind) = self.parse_array_expression() { + return kind; + } + if let Some(kind) = self.parse_parentheses_expression() { return kind; } @@ -43,11 +50,68 @@ impl<'a> Parser<'a> { return ExpressionKind::Block(kind); } - self.push_error(ParserErrorReason::ExpectedExpression, self.current_token_span); - ExpressionKind::Error } + fn parse_array_expression(&mut self) -> Option { + if !self.eat_left_bracket() { + return None; + } + + if self.eat_right_bracket() { + return Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard( + Vec::new(), + )))); + } + + let start_span = self.current_token_span; + let first_expr = self.parse_expression(); + if self.current_token_span == start_span { + return Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard( + Vec::new(), + )))); + } + + if self.eat_semicolon() { + let length = self.parse_expression(); + if !self.eat_right_bracket() { + self.push_error( + ParserErrorReason::ExpectedBracketAfterArray, + self.current_token_span, + ); + } + return Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { + repeated_element: Box::new(first_expr), + length: Box::new(length), + }))); + } + + let mut exprs = vec![first_expr]; + let mut trailing_comma = self.eat_comma(); + loop { + if self.eat_right_bracket() { + break; + } + + let start_span = self.current_token_span; + let expr = self.parse_expression(); + if self.current_token_span == start_span { + self.eat_right_brace(); + break; + } + + if !trailing_comma { + self.push_error(ParserErrorReason::MissingCommaSeparatingExpressions, start_span); + } + + exprs.push(expr); + + trailing_comma = self.eat_commas(); + } + + Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs)))) + } + fn parse_parentheses_expression(&mut self) -> Option { if !self.eat_left_paren() { return None; @@ -63,6 +127,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let expr = self.parse_expression(); if let ExpressionKind::Error = expr.kind { + self.push_error(ParserErrorReason::ExpectedExpression, start_span); self.eat_right_paren(); break; } @@ -112,7 +177,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ExpressionKind, Literal, StatementKind}, + ast::{ArrayLiteral, ExpressionKind, Literal, StatementKind}, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, @@ -281,4 +346,63 @@ mod tests { let reason = get_single_error(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::MissingCommaSeparatingExpressions)); } + + #[test] + fn parses_empty_array_expression() { + let src = "[]"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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"); + } } From cf9e7096abb54589e292a8c1ab99fb869799db1f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 13:18:16 -0300 Subject: [PATCH 071/229] Parse slices --- compiler/noirc_frontend/src/parser/errors.rs | 2 + .../src/parser/parser/expression.rs | 56 ++++++++++++++----- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 08a6cbe825a..3e2b1c837b2 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -59,6 +59,8 @@ pub enum ParserErrorReason { ExpectedBracketAfterArrayType, #[error("Expected `]` after array")] ExpectedBracketAfterArray, + #[error("Expected `]` after slice")] + ExpectedBracketAfterSlice, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 11fab859b49..b37c22f5fb6 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -4,6 +4,7 @@ use crate::{ StatementKind, }, parser::ParserErrorReason, + token::Token, }; use super::Parser; @@ -42,6 +43,14 @@ impl<'a> Parser<'a> { return kind; } + if self.eat(Token::Ampersand) { + if let Some(array_literal) = self.parse_array_literal(true) { + return ExpressionKind::Literal(Literal::Slice(array_literal)); + } + + todo!() + } + if let Some(kind) = self.parse_parentheses_expression() { return kind; } @@ -54,36 +63,44 @@ impl<'a> Parser<'a> { } fn parse_array_expression(&mut self) -> Option { + self.parse_array_literal(false) + .map(|array_literal| ExpressionKind::Literal(Literal::Array(array_literal))) + } + + fn parse_array_literal(&mut self, is_slice: bool) -> Option { if !self.eat_left_bracket() { return None; } if self.eat_right_bracket() { - return Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard( - Vec::new(), - )))); + return Some(ArrayLiteral::Standard(Vec::new())); } let start_span = self.current_token_span; let first_expr = self.parse_expression(); if self.current_token_span == start_span { - return Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard( - Vec::new(), - )))); + return Some(ArrayLiteral::Standard(Vec::new())); } if self.eat_semicolon() { let length = self.parse_expression(); if !self.eat_right_bracket() { - self.push_error( - ParserErrorReason::ExpectedBracketAfterArray, - self.current_token_span, - ); + if is_slice { + self.push_error( + ParserErrorReason::ExpectedBracketAfterSlice, + self.current_token_span, + ); + } else { + self.push_error( + ParserErrorReason::ExpectedBracketAfterArray, + self.current_token_span, + ); + } } - return Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { + return Some(ArrayLiteral::Repeated { repeated_element: Box::new(first_expr), length: Box::new(length), - }))); + }); } let mut exprs = vec![first_expr]; @@ -109,7 +126,7 @@ impl<'a> Parser<'a> { trailing_comma = self.eat_commas(); } - Some(ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs)))) + Some(ArrayLiteral::Standard(exprs)) } fn parse_parentheses_expression(&mut self) -> Option { @@ -405,4 +422,17 @@ mod tests { assert_eq!(repeated_element.to_string(), "1"); assert_eq!(length.to_string(), "10"); } + + #[test] + fn parses_empty_slice_expression() { + let src = "&[]"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr.kind + else { + panic!("Expected slice literal"); + }; + assert!(exprs.is_empty()); + } } From 22baad5eb7dcb447f6c6cc71bd054e77a233750a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 13:27:46 -0300 Subject: [PATCH 072/229] About to start parsing statements --- compiler/noirc_frontend/src/parser/parser.rs | 7 ++----- .../noirc_frontend/src/parser/parser/expression.rs | 10 +++------- .../noirc_frontend/src/parser/parser/statement.rs | 11 +++++++++++ 3 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/statement.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index c277e6e8488..e38fcfebcb8 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -2,7 +2,7 @@ use acvm::FieldElement; use noirc_errors::Span; use crate::{ - ast::{Ident, LValue, Statement}, + ast::{Ident, LValue}, lexer::{Lexer, SpannedTokenResult}, token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; @@ -22,6 +22,7 @@ mod modifiers; mod module; mod path; mod pattern; +mod statement; mod structs; mod tests; mod traits; @@ -112,10 +113,6 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_statement(&mut self) -> Statement { - todo!("Parser") - } - pub(crate) fn parse_lvalue(&mut self) -> LValue { todo!("Parser") } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index b37c22f5fb6..0e46af9f2b4 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,8 +1,5 @@ use crate::{ - ast::{ - ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, Statement, - StatementKind, - }, + ast::{ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal}, parser::ParserErrorReason, token::Token, }; @@ -179,9 +176,8 @@ impl<'a> Parser<'a> { return Some(BlockExpression { statements }); } - let expr = self.parse_expression(); - let span = expr.span; - statements.push(Statement { kind: StatementKind::Expression(expr), span }); + let statement = self.parse_statement(); + statements.push(statement); if !self.eat_right_brace() { // TODO: error (for later) 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..85bb514d97f --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -0,0 +1,11 @@ +use crate::ast::{Statement, StatementKind}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_statement(&mut self) -> Statement { + let expr = self.parse_expression(); + let span = expr.span; + Statement { kind: StatementKind::Expression(expr), span } + } +} From 1677124633822c03ce5e57b33eead59eb63c39af Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 13:40:45 -0300 Subject: [PATCH 073/229] Parse let statement --- .../src/parser/parser/statement.rs | 82 ++++++++++++++++++- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 85bb514d97f..afce842c57f 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -1,11 +1,85 @@ -use crate::ast::{Statement, StatementKind}; +use noirc_errors::Span; + +use crate::{ + ast::{Expression, ExpressionKind, LetStatement, Statement, StatementKind}, + token::{Attribute, Keyword}, +}; use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_statement(&mut self) -> Statement { - let expr = self.parse_expression(); - let span = expr.span; - Statement { kind: StatementKind::Expression(expr), span } + let attributes = self.parse_attributes(); + + let start_span = self.current_token_span; + let kind = self.parse_statement_kind(attributes); + let span = self.span_since(start_span); + Statement { kind, span } + } + + fn parse_statement_kind(&mut self, attributes: Vec<(Attribute, Span)>) -> StatementKind { + if let Some(let_statement) = self.parse_let_statement(attributes) { + return StatementKind::Let(let_statement); + } + + StatementKind::Expression(self.parse_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(); + let r#type = self.parse_optional_type_annotation(); + let expression = if self.eat_assign() { + self.parse_expression() + } else { + // TODO: error + Expression { kind: ExpressionKind::Error, span: self.current_token_span } + }; + + Some(LetStatement { pattern, r#type, expression, attributes, comptime: false }) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{StatementKind, UnresolvedTypeData}, + parser::Parser, + }; + + #[test] + fn parses_let_statement_no_type() { + let src = "let x = 1;"; + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + 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); } } From 6219dce5e3e78aef1af6b2a40fd701bc208463cd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 13:50:06 -0300 Subject: [PATCH 074/229] Parse break and continue --- .../src/parser/parser/statement.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index afce842c57f..102033f5ca8 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -18,6 +18,14 @@ impl<'a> Parser<'a> { } fn parse_statement_kind(&mut self, attributes: Vec<(Attribute, Span)>) -> StatementKind { + if self.eat_keyword(Keyword::Break) { + return StatementKind::Break; + } + + if self.eat_keyword(Keyword::Continue) { + return StatementKind::Continue; + } + if let Some(let_statement) = self.parse_let_statement(attributes) { return StatementKind::Let(let_statement); } @@ -51,6 +59,24 @@ mod tests { parser::Parser, }; + #[test] + fn parses_break() { + let src = "break"; + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + assert!(matches!(statement.kind, StatementKind::Break)); + } + + #[test] + fn parses_continue() { + let src = "continue"; + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + assert!(matches!(statement.kind, StatementKind::Continue)); + } + #[test] fn parses_let_statement_no_type() { let src = "let x = 1;"; From 17f407499f3d7a2bf8d4b5ff72f579b429e87c10 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 13:53:11 -0300 Subject: [PATCH 075/229] Parse interned statements --- compiler/noirc_frontend/src/parser/parser/statement.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 102033f5ca8..2dca01feb87 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Expression, ExpressionKind, LetStatement, Statement, StatementKind}, - token::{Attribute, Keyword}, + token::{Attribute, Keyword, Token, TokenKind}, }; use super::Parser; @@ -18,6 +18,13 @@ impl<'a> Parser<'a> { } fn parse_statement_kind(&mut self, attributes: Vec<(Attribute, Span)>) -> StatementKind { + if let Some(token) = self.eat_kind(TokenKind::InternedStatement) { + match token.into_token() { + Token::InternedStatement(statement) => return StatementKind::Interned(statement), + _ => unreachable!(), + } + } + if self.eat_keyword(Keyword::Break) { return StatementKind::Break; } From f3f389f03b8553d787475712ee4ac1325bc35654 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 14:00:46 -0300 Subject: [PATCH 076/229] Parse multiple statements in block --- .../src/parser/parser/expression.rs | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 0e46af9f2b4..3a7b8d9f1e8 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -172,15 +172,23 @@ impl<'a> Parser<'a> { let mut statements = Vec::new(); - if self.eat_right_brace() { - return Some(BlockExpression { statements }); - } + loop { + if self.eat_right_brace() { + break; + } - let statement = self.parse_statement(); - statements.push(statement); + let start_span = self.current_token_span; + let statement = self.parse_statement(); + if self.current_token_span == start_span { + // TODO: error? + self.eat_right_brace(); + break; + } - if !self.eat_right_brace() { - // TODO: error (for later) + statements.push(statement); + + // TODO: error if missing semicolon and statement requires one and is not the last one in the block + self.eat_semicolons(); } Some(BlockExpression { statements }) @@ -334,6 +342,27 @@ mod tests { assert!(!negative); } + #[test] + fn parses_block_expression_with_multiple_statements() { + let src = " + { + let x = 1; + let y = 2; + 3 + } + "; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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_unclosed_parentheses() { let src = " From 66b0d21ca31bafecade4fe925059dc2fa2c32864 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 14:14:07 -0300 Subject: [PATCH 077/229] Parse assert and assert_eq --- compiler/noirc_frontend/src/parser/errors.rs | 2 + compiler/noirc_frontend/src/parser/parser.rs | 1 + .../noirc_frontend/src/parser/parser/call.rs | 37 +++++++++++ .../src/parser/parser/statement.rs | 66 +++++++++++++++++-- 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/call.rs diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 3e2b1c837b2..5edbab4ed48 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -31,6 +31,8 @@ pub enum ParserErrorReason { MissingCommaSeparatingTraitBounds, #[error("Expected a `+` separating these two trait bounds")] MissingPlusSeparatingTraitBounds, + #[error("Expected a `,` separating these two arguments")] + MissingCommaSeparatingArguments, #[error("Expected an identifier after `fn`")] ExpectedIdentifierAfterFn, #[error("Missing type for function parameter")] diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index e38fcfebcb8..83af5554d42 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -10,6 +10,7 @@ use crate::{ use super::{ParsedModule, ParserError, ParserErrorReason}; mod attributes; +mod call; mod doc_comments; mod expression; mod function; diff --git a/compiler/noirc_frontend/src/parser/parser/call.rs b/compiler/noirc_frontend/src/parser/parser/call.rs new file mode 100644 index 00000000000..15e0dd1a0dd --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/call.rs @@ -0,0 +1,37 @@ +use crate::{ast::Expression, parser::ParserErrorReason}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(crate) fn parse_arguments(&mut self) -> Vec { + if !self.eat_left_paren() { + // TODO: error + return Vec::new(); + } + + if self.eat_right_paren() { + return Vec::new(); + } + + let mut arguments = Vec::new(); + let mut trailing_comma = false; + loop { + let start_span = self.current_token_span; + let expr = self.parse_expression(); + if start_span == self.current_token_span { + self.eat_right_paren(); + break; + } + + if !trailing_comma && !arguments.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingArguments, start_span); + } + + arguments.push(expr); + + trailing_comma = self.eat_comma(); + } + + arguments + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 2dca01feb87..36042b6e93e 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -1,7 +1,10 @@ use noirc_errors::Span; use crate::{ - ast::{Expression, ExpressionKind, LetStatement, Statement, StatementKind}, + ast::{ + ConstrainKind, ConstrainStatement, Expression, ExpressionKind, LetStatement, Statement, + StatementKind, + }, token::{Attribute, Keyword, Token, TokenKind}, }; @@ -37,6 +40,10 @@ impl<'a> Parser<'a> { return StatementKind::Let(let_statement); } + if let Some(constrain) = self.parse_constrain_statement() { + return StatementKind::Constrain(constrain); + } + StatementKind::Expression(self.parse_expression()) } @@ -57,12 +64,39 @@ impl<'a> Parser<'a> { Some(LetStatement { pattern, r#type, expression, attributes, comptime: false }) } + + 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(); + ConstrainStatement { kind, arguments, span: self.span_since(start_span) } + } + ConstrainKind::Constrain => todo!(), + }) + } + + 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::{StatementKind, UnresolvedTypeData}, + ast::{ConstrainKind, StatementKind, UnresolvedTypeData}, parser::Parser, }; @@ -93,7 +127,6 @@ mod tests { 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"); @@ -109,10 +142,35 @@ mod tests { 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 mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + let StatementKind::Constrain(constrain) = statement.kind else { + panic!("Expected constrain statement"); + }; + assert_eq!(constrain.kind, ConstrainKind::AssertEq); + assert_eq!(constrain.arguments.len(), 3); + } } From a1a19311335b8244ae7d1473a7970bde342d0373 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 14:18:07 -0300 Subject: [PATCH 078/229] Parse constrain --- .../src/parser/parser/statement.rs | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 36042b6e93e..05684735269 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -5,6 +5,7 @@ use crate::{ ConstrainKind, ConstrainStatement, Expression, ExpressionKind, LetStatement, Statement, StatementKind, }, + parser::ParserErrorReason, token::{Attribute, Keyword, Token, TokenKind}, }; @@ -76,7 +77,16 @@ impl<'a> Parser<'a> { let arguments = self.parse_arguments(); ConstrainStatement { kind, arguments, span: self.span_since(start_span) } } - ConstrainKind::Constrain => todo!(), + ConstrainKind::Constrain => { + self.push_error(ParserErrorReason::ConstrainDeprecated, self.previous_token_span); + + let expression = self.parse_expression(); + ConstrainStatement { + kind, + arguments: vec![expression], + span: self.span_since(start_span), + } + } }) } @@ -97,7 +107,10 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ConstrainKind, StatementKind, UnresolvedTypeData}, - parser::Parser, + parser::{ + parser::tests::{get_single_error, get_source_with_error_span}, + Parser, ParserErrorReason, + }, }; #[test] @@ -173,4 +186,23 @@ mod tests { 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(); + 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(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ConstrainDeprecated)); + } } From f5c448ca482c9d3532dfc6d120a3d423c42c4ecb Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 14:34:17 -0300 Subject: [PATCH 079/229] Parse variable --- .../src/parser/parser/expression.rs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 3a7b8d9f1e8..98ec8dd6929 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -45,7 +45,8 @@ impl<'a> Parser<'a> { return ExpressionKind::Literal(Literal::Slice(array_literal)); } - todo!() + // TODO: parse this + return ExpressionKind::Error; } if let Some(kind) = self.parse_parentheses_expression() { @@ -56,6 +57,11 @@ impl<'a> Parser<'a> { return ExpressionKind::Block(kind); } + let (path, trailing_double_colon) = self.parse_path_impl(false); // do not allow turbofish + if !path.is_empty() { + return ExpressionKind::Variable(path); + } + ExpressionKind::Error } @@ -460,4 +466,28 @@ mod tests { }; assert!(exprs.is_empty()); } + + #[test] + fn parses_variable_ident() { + let src = "foo"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Variable(path) = expr.kind else { + panic!("Expected variable"); + }; + assert_eq!(path.to_string(), "foo::bar"); + } } From f18575074bfed57160d3b6ee1168205cbebcb952 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 14:38:58 -0300 Subject: [PATCH 080/229] Parse mutable reference --- .../src/parser/parser/expression.rs | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 98ec8dd6929..6d7cf81e26b 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,7 +1,10 @@ use crate::{ - ast::{ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal}, + ast::{ + ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, PrefixExpression, + UnaryOp, + }, parser::ParserErrorReason, - token::Token, + token::{Keyword, Token}, }; use super::Parser; @@ -45,8 +48,13 @@ impl<'a> Parser<'a> { return ExpressionKind::Literal(Literal::Slice(array_literal)); } - // TODO: parse this - return ExpressionKind::Error; + if !self.eat_keyword(Keyword::Mut) { + // TODO: error (expected `mut` after `&`) + } + + let rhs = self.parse_expression(); + let operator = UnaryOp::MutableReference; + return ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); } if let Some(kind) = self.parse_parentheses_expression() { @@ -204,7 +212,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ArrayLiteral, ExpressionKind, Literal, StatementKind}, + ast::{ArrayLiteral, ExpressionKind, Literal, StatementKind, UnaryOp}, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, @@ -490,4 +498,21 @@ mod tests { }; assert_eq!(path.to_string(), "foo::bar"); } + + #[test] + fn parses_mutable_ref() { + let src = "&mut foo"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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"); + } } From 62620403765e2741e1feac1a4792373d2c04f9f2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 14:45:15 -0300 Subject: [PATCH 081/229] Parse unsafe expression --- .../src/parser/parser/expression.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 6d7cf81e26b..4a63ed49b23 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -57,6 +57,16 @@ impl<'a> Parser<'a> { return ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); } + if self.eat_keyword(Keyword::Unsafe) { + let start_span = self.span_since(self.previous_token_span); + if let Some(block) = self.parse_block_expression() { + return ExpressionKind::Unsafe(block, self.span_since(start_span)); + } else { + // TODO: error (expected block after unsafe) + return self.parse_expression_kind(); + }; + } + if let Some(kind) = self.parse_parentheses_expression() { return kind; } @@ -377,6 +387,18 @@ mod tests { assert_eq!(block.statements[2].kind.to_string(), "3"); } + #[test] + fn parses_unsafe_expression() { + let src = "unsafe { 1 }"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Unsafe(block, _) = expr.kind else { + panic!("Expected unsafe expression"); + }; + assert_eq!(block.statements.len(), 1); + } + #[test] fn parses_unclosed_parentheses() { let src = " From b407ad66d84e5a32932fd2c3ce27db73b080b0c8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 14:47:59 -0300 Subject: [PATCH 082/229] Parse quote --- compiler/noirc_frontend/src/parser/parser.rs | 13 +++++++++++++ .../src/parser/parser/expression.rs | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 83af5554d42..dd3fcdf9470 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -264,6 +264,19 @@ impl<'a> Parser<'a> { } } + fn eat_quote(&mut self) -> Option { + if matches!(self.token.token(), Token::Quote(..)) { + let token = std::mem::take(&mut self.token); + self.next_token(); + match token.into_token() { + Token::Quote(tokens) => Some(tokens), + _ => unreachable!(), + } + } else { + None + } + } + fn eat_comma(&mut self) -> bool { self.eat(Token::Comma) } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 4a63ed49b23..44e675f4c7e 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -67,6 +67,10 @@ impl<'a> Parser<'a> { }; } + if let Some(tokens) = self.eat_quote() { + return ExpressionKind::Quote(tokens); + } + if let Some(kind) = self.parse_parentheses_expression() { return kind; } @@ -537,4 +541,16 @@ mod tests { }; assert_eq!(path.to_string(), "foo"); } + + #[test] + fn parses_quote() { + let src = "quote { 1 }"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Quote(tokens) = expr.kind else { + panic!("Expected quote expression"); + }; + assert_eq!(tokens.0.len(), 1); + } } From b01d0a6ca6e6d8649fc983d3bc23fa20153d5b56 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 15:52:28 -0300 Subject: [PATCH 083/229] Start parsing expressions the correct way, and introduce look-ahead --- compiler/noirc_frontend/src/parser/parser.rs | 37 +++-- .../noirc_frontend/src/parser/parser/call.rs | 9 +- .../src/parser/parser/expression.rs | 137 ++++++++++++------ .../src/parser/parser/statement.rs | 5 + 4 files changed, 129 insertions(+), 59 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index dd3fcdf9470..fa23b022c75 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -76,7 +76,12 @@ impl<'a> TokenStream<'a> { pub struct Parser<'a> { errors: Vec, tokens: TokenStream<'a>, + + // We always have one look-ahead token to see if we get `&mut` or just `&` + // (`&` and `mut` are two separate tokens) token: SpannedToken, + next_token: SpannedToken, + current_token_span: Span, previous_token_span: Span, } @@ -99,11 +104,12 @@ impl<'a> Parser<'a> { let mut parser = Self { errors: Vec::new(), tokens, - token: SpannedToken::new(Token::EOF, Default::default()), + token: SpannedToken::default(), + next_token: SpannedToken::default(), current_token_span: Default::default(), previous_token_span: Default::default(), }; - parser.next_token(); + parser.read_two_first_tokens(); parser } @@ -119,23 +125,30 @@ impl<'a> Parser<'a> { } fn next_token(&mut self) { - loop { - self.previous_token_span = self.current_token_span; + self.previous_token_span = self.current_token_span; + let token = self.read_token_internal(); + let next_token = std::mem::take(&mut self.next_token); + self.token = next_token; + self.next_token = token; + self.current_token_span = self.token.to_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 read_token_internal(&mut self) -> SpannedToken { + loop { let token = self.tokens.next(); if let Some(token) = token { match token { - Ok(token) => { - self.current_token_span = token.to_span(); - self.token = token; - break; - } + Ok(token) => return token, Err(lexer_error) => self.errors.push(lexer_error.into()), } } else { - self.token = SpannedToken::new(Token::EOF, Default::default()); - self.current_token_span = Default::default(); - break; + return SpannedToken::default(); } } } diff --git a/compiler/noirc_frontend/src/parser/parser/call.rs b/compiler/noirc_frontend/src/parser/parser/call.rs index 15e0dd1a0dd..8dfdded9c22 100644 --- a/compiler/noirc_frontend/src/parser/parser/call.rs +++ b/compiler/noirc_frontend/src/parser/parser/call.rs @@ -3,14 +3,13 @@ use crate::{ast::Expression, parser::ParserErrorReason}; use super::Parser; impl<'a> Parser<'a> { - pub(crate) fn parse_arguments(&mut self) -> Vec { + pub(crate) fn parse_arguments(&mut self) -> Option> { if !self.eat_left_paren() { - // TODO: error - return Vec::new(); + return None; } if self.eat_right_paren() { - return Vec::new(); + return Some(Vec::new()); } let mut arguments = Vec::new(); @@ -32,6 +31,6 @@ impl<'a> Parser<'a> { trailing_comma = self.eat_comma(); } - arguments + Some(arguments) } } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 44e675f4c7e..24fe171c3d2 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -9,82 +9,122 @@ use crate::{ use super::Parser; +// term -> atom_or_right_unary -> atom + impl<'a> Parser<'a> { pub(crate) fn parse_expression(&mut self) -> Expression { let start_span = self.current_token_span; - let kind = self.parse_expression_kind(); + let kind = self.parse_term(); let span = self.span_since(start_span); - Expression { kind, span } } - fn parse_expression_kind(&mut self) -> ExpressionKind { - if let Some(bool) = self.eat_bool() { - return ExpressionKind::Literal(Literal::Bool(bool)); + fn parse_term(&mut self) -> ExpressionKind { + if self.token.token() == &Token::Ampersand + && self.next_token.token() == &Token::Keyword(Keyword::Mut) + { + self.next_token(); + self.next_token(); + let start_span = self.current_token_span; + let term = self.parse_term(); + let rhs = Expression { kind: term, span: self.span_since(start_span) }; + let operator = UnaryOp::MutableReference; + return ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); } - if let Some(int) = self.eat_int() { - return ExpressionKind::integer(int); + self.parse_atom_or_unary_right() + } + + fn parse_atom_or_unary_right(&mut self) -> ExpressionKind { + self.parse_atom() + } + + fn parse_atom(&mut self) -> ExpressionKind { + if let Some(literal) = self.parse_literal() { + return literal; } - if let Some(string) = self.eat_str() { - return ExpressionKind::Literal(Literal::Str(string)); + if let Some(kind) = self.parse_parentheses_expression() { + return kind; } - if let Some((string, n)) = self.eat_raw_str() { - return ExpressionKind::Literal(Literal::RawStr(string, n)); + if self.eat_keyword(Keyword::Unsafe) { + let start_span = self.span_since(self.previous_token_span); + if let Some(block) = self.parse_block_expression() { + return ExpressionKind::Unsafe(block, self.span_since(start_span)); + } else { + return ExpressionKind::Error; + }; } - if let Some(string) = self.eat_fmt_str() { - return ExpressionKind::Literal(Literal::FmtStr(string)); + let (path, trailing_double_colon) = self.parse_path_impl(false); // do not allow turbofish + if !path.is_empty() { + return ExpressionKind::Variable(path); } - if let Some(kind) = self.parse_array_expression() { - return kind; + ExpressionKind::Error + } + + fn parse_literal(&mut self) -> Option { + if let Some(bool) = self.eat_bool() { + return Some(ExpressionKind::Literal(Literal::Bool(bool))); } - if self.eat(Token::Ampersand) { - if let Some(array_literal) = self.parse_array_literal(true) { - return ExpressionKind::Literal(Literal::Slice(array_literal)); - } + if let Some(int) = self.eat_int() { + return Some(ExpressionKind::integer(int)); + } - if !self.eat_keyword(Keyword::Mut) { - // TODO: error (expected `mut` after `&`) - } + if let Some(string) = self.eat_str() { + return Some(ExpressionKind::Literal(Literal::Str(string))); + } - let rhs = self.parse_expression(); - let operator = UnaryOp::MutableReference; - return ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); + if let Some((string, n)) = self.eat_raw_str() { + return Some(ExpressionKind::Literal(Literal::RawStr(string, n))); } - if self.eat_keyword(Keyword::Unsafe) { - let start_span = self.span_since(self.previous_token_span); - if let Some(block) = self.parse_block_expression() { - return ExpressionKind::Unsafe(block, self.span_since(start_span)); - } else { - // TODO: error (expected block after unsafe) - return self.parse_expression_kind(); - }; + if let Some(string) = self.eat_fmt_str() { + return Some(ExpressionKind::Literal(Literal::FmtStr(string))); } if let Some(tokens) = self.eat_quote() { - return ExpressionKind::Quote(tokens); + return Some(ExpressionKind::Quote(tokens)); } - if let Some(kind) = self.parse_parentheses_expression() { - return kind; + if let Some(kind) = self.parse_array_expression() { + return Some(kind); } - if let Some(kind) = self.parse_block_expression() { - return ExpressionKind::Block(kind); + // Check if it's `&[` + if self.token.token() == &Token::Ampersand && self.next_token.token() == &Token::LeftBracket + { + self.next_token(); + + return Some(ExpressionKind::Literal(Literal::Slice( + self.parse_array_literal(true).unwrap(), + ))); } - let (path, trailing_double_colon) = self.parse_path_impl(false); // do not allow turbofish - if !path.is_empty() { - return ExpressionKind::Variable(path); + if let Some(kind) = self.parse_block_expression() { + return Some(ExpressionKind::Block(kind)); } - ExpressionKind::Error + // TODO: parse these too + // if_expr(expr_no_constructors, statement.clone()), + // if allow_constructors { + // constructor(expr_parser.clone()).boxed() + // } else { + // nothing().boxed() + // }, + // lambdas::lambda(expr_parser.clone()), + // comptime_expr(statement.clone()), + // unquote(expr_parser.clone()), + // as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), + // type_path(parse_type()), + // macro_quote_marker(), + // interned_expr(), + // interned_statement_expr(), + + None } fn parse_array_expression(&mut self) -> Option { @@ -553,4 +593,17 @@ mod tests { }; assert_eq!(tokens.0.len(), 1); } + + // #[test] + // fn parses_call() { + // let src = "foo(1, 2)"; + // let mut parser = Parser::for_str(src); + // let expr = parser.parse_expression(); + // assert!(parser.errors.is_empty()); + // let ExpressionKind::Call(call) = expr.kind else { + // panic!("Expected call expression"); + // }; + // assert_eq!(call.func.to_string(), "foo"); + // assert_eq!(call.arguments.len(), 2); + // } } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 05684735269..93313c0b079 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -75,6 +75,11 @@ impl<'a> Parser<'a> { Some(match kind { ConstrainKind::Assert | ConstrainKind::AssertEq => { let arguments = self.parse_arguments(); + if arguments.is_none() { + // TODO: error (expected arguments to assert/assert_eq) + } + let arguments = arguments.unwrap_or_default(); + ConstrainStatement { kind, arguments, span: self.span_since(start_span) } } ConstrainKind::Constrain => { From 69524527a99254f2f6107846851327417f2fbafa Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 16:04:39 -0300 Subject: [PATCH 084/229] Parse all prefix expressions --- .../src/parser/parser/expression.rs | 86 +++++++++++++++++-- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 24fe171c3d2..0033d779893 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -14,25 +14,42 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_expression(&mut self) -> Expression { let start_span = self.current_token_span; - let kind = self.parse_term(); + let kind = self.parse_term_kind(); let span = self.span_since(start_span); Expression { kind, span } } - fn parse_term(&mut self) -> ExpressionKind { + fn parse_term(&mut self) -> Expression { + let start_span = self.current_token_span; + let kind = self.parse_term_kind(); + Expression { kind, span: self.span_since(start_span) } + } + + fn parse_term_kind(&mut self) -> ExpressionKind { + if let Some(operator) = self.parse_unary_op() { + let rhs = self.parse_term(); + return ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); + } + + self.parse_atom_or_unary_right() + } + + fn parse_unary_op(&mut self) -> Option { if self.token.token() == &Token::Ampersand && self.next_token.token() == &Token::Keyword(Keyword::Mut) { self.next_token(); self.next_token(); - let start_span = self.current_token_span; - let term = self.parse_term(); - let rhs = Expression { kind: term, span: self.span_since(start_span) }; - let operator = UnaryOp::MutableReference; - return ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); + 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 } - - self.parse_atom_or_unary_right() } fn parse_atom_or_unary_right(&mut self) -> ExpressionKind { @@ -582,6 +599,57 @@ mod tests { assert_eq!(path.to_string(), "foo"); } + #[test] + fn parses_minus() { + let src = "-foo"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 }"; From 407e84eb6000bc3218ae5d3ec67e289265a32e37 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 16:29:07 -0300 Subject: [PATCH 085/229] Parse macro calls too --- .../src/parser/parser/expression.rs | 77 +++++++++++++++---- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 0033d779893..60231a7a216 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,7 +1,7 @@ use crate::{ ast::{ - ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, PrefixExpression, - UnaryOp, + ArrayLiteral, BlockExpression, CallExpression, Expression, ExpressionKind, Literal, + PrefixExpression, UnaryOp, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -53,10 +53,40 @@ impl<'a> Parser<'a> { } fn parse_atom_or_unary_right(&mut self) -> ExpressionKind { - self.parse_atom() + let start_span = self.current_token_span; + let mut atom = self.parse_atom(); + + loop { + let is_macro_call = + self.token.token() == &Token::Bang && self.next_token.token() == &Token::LeftParen; + if is_macro_call { + // Next `self.parse_arguments` will return `Some(...)` + self.next_token(); + } + + if let Some(arguments) = self.parse_arguments() { + let kind = ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(atom), + arguments, + is_macro_call, + })); + let span = self.span_since(start_span); + atom = Expression { kind, span }; + } else { + break; + } + } + + atom.kind + } + + fn parse_atom(&mut self) -> Expression { + let start_span = self.current_token_span; + let kind = self.parse_atom_kind(); + Expression { kind, span: self.span_since(start_span) } } - fn parse_atom(&mut self) -> ExpressionKind { + fn parse_atom_kind(&mut self) -> ExpressionKind { if let Some(literal) = self.parse_literal() { return literal; } @@ -662,16 +692,31 @@ mod tests { assert_eq!(tokens.0.len(), 1); } - // #[test] - // fn parses_call() { - // let src = "foo(1, 2)"; - // let mut parser = Parser::for_str(src); - // let expr = parser.parse_expression(); - // assert!(parser.errors.is_empty()); - // let ExpressionKind::Call(call) = expr.kind else { - // panic!("Expected call expression"); - // }; - // assert_eq!(call.func.to_string(), "foo"); - // assert_eq!(call.arguments.len(), 2); - // } + #[test] + fn parses_call() { + let src = "foo(1, 2)"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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); + } } From b3d6355756c96ca032daee91622939a2f849cad8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 16:38:18 -0300 Subject: [PATCH 086/229] Parse member access --- compiler/noirc_frontend/src/parser/parser.rs | 4 ++ .../src/parser/parser/expression.rs | 40 +++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index fa23b022c75..a113d0d51e9 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -368,6 +368,10 @@ impl<'a> Parser<'a> { self.eat(Token::Plus) } + fn eat_dot(&mut self) -> bool { + self.eat(Token::Dot) + } + fn eat(&mut self, token: Token) -> bool { if self.token.token() == &token { self.next_token(); diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 60231a7a216..5d8b4b414eb 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,7 +1,7 @@ use crate::{ ast::{ - ArrayLiteral, BlockExpression, CallExpression, Expression, ExpressionKind, Literal, - PrefixExpression, UnaryOp, + ArrayLiteral, BlockExpression, CallExpression, Expression, ExpressionKind, Ident, Literal, + MemberAccessExpression, PrefixExpression, UnaryOp, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -72,9 +72,28 @@ impl<'a> Parser<'a> { })); let span = self.span_since(start_span); atom = Expression { kind, span }; - } else { - break; + continue; + } + + if self.eat_dot() { + let field_name = if let Some(ident) = self.eat_ident() { + ident + } else if let Some(int) = self.eat_int() { + Ident::new(int.to_string(), self.previous_token_span) + } else { + // TODO: error + Ident::default() + }; + let kind = ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: atom, + rhs: field_name, + })); + let span = self.span_since(start_span); + atom = Expression { kind, span }; + continue; } + + break; } atom.kind @@ -719,4 +738,17 @@ mod tests { assert_eq!(call.arguments.len(), 2); assert!(call.is_macro_call); } + + #[test] + fn parses_member_access() { + let src = "foo.bar"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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"); + } } From 5cd00576eec3b4f4da97bbad5675ba4d377dfeb1 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 16:42:52 -0300 Subject: [PATCH 087/229] Parse method calls --- .../src/parser/parser/expression.rs | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 5d8b4b414eb..48fa8c11711 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,7 +1,7 @@ use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, Expression, ExpressionKind, Ident, Literal, - MemberAccessExpression, PrefixExpression, UnaryOp, + MemberAccessExpression, MethodCallExpression, PrefixExpression, UnaryOp, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -84,12 +84,34 @@ impl<'a> Parser<'a> { // TODO: error Ident::default() }; - let kind = ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: atom, - rhs: field_name, - })); - let span = self.span_since(start_span); - atom = Expression { kind, span }; + + // TODO: parse generics + + let is_macro_call = self.token.token() == &Token::Bang + && self.next_token.token() == &Token::LeftParen; + if is_macro_call { + // Next `self.parse_arguments` will return `Some(...)` + self.next_token(); + } + + if let Some(arguments) = self.parse_arguments() { + let kind = ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: atom, + method_name: field_name, + generics: None, + arguments, + is_macro_call, + })); + let span = self.span_since(start_span); + atom = Expression { kind, span }; + } else { + let kind = ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: atom, + rhs: field_name, + })); + let span = self.span_since(start_span); + atom = Expression { kind, span }; + } continue; } @@ -751,4 +773,36 @@ mod tests { 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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_macro_call() { + let src = "foo.bar!(1, 2)"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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()); + } } From 611e5ee5303d29665ea825e8d3adc921e8ad0ade Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 16:51:53 -0300 Subject: [PATCH 088/229] Parse turbofish --- .../src/parser/parser/expression.rs | 44 +++++++++++++++++-- .../noirc_frontend/src/parser/parser/path.rs | 2 +- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 48fa8c11711..5f7e059907c 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -85,7 +85,15 @@ impl<'a> Parser<'a> { Ident::default() }; - // TODO: parse generics + let generics = if self.eat_double_colon() { + let generics = self.parse_path_generics(); + if generics.is_none() { + // TODO: error (found `::` but not `::<...>`) + } + generics + } else { + None + }; let is_macro_call = self.token.token() == &Token::Bang && self.next_token.token() == &Token::LeftParen; @@ -98,7 +106,7 @@ impl<'a> Parser<'a> { let kind = ExpressionKind::MethodCall(Box::new(MethodCallExpression { object: atom, method_name: field_name, - generics: None, + generics, arguments, is_macro_call, })); @@ -145,7 +153,7 @@ impl<'a> Parser<'a> { }; } - let (path, trailing_double_colon) = self.parse_path_impl(false); // do not allow turbofish + let path = self.parse_path(); if !path.is_empty() { return ExpressionKind::Variable(path); } @@ -747,6 +755,20 @@ mod tests { assert!(!call.is_macro_call); } + #[test] + fn parses_call_with_turbofish() { + let src = "foo::(1, 2)"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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)"; @@ -790,6 +812,22 @@ mod tests { assert!(method_call.generics.is_none()); } + #[test] + fn parses_method_call_with_turbofish() { + let src = "foo.bar::(1, 2)"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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)"; diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 27b69ebeff0..f6a0de4d35c 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -95,7 +95,7 @@ impl<'a> Parser<'a> { Path { segments, kind: PathKind::Plain, span: self.span_since(start_span) } } - fn parse_path_generics(&mut self) -> Option> { + pub(super) fn parse_path_generics(&mut self) -> Option> { if !self.eat_less() { return None; } From 3ebbec2b8673efc988324976a9cbced20770d2b1 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 16:57:29 -0300 Subject: [PATCH 089/229] Fix bug parsing turbofish --- compiler/noirc_frontend/src/parser/parser/path.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index f6a0de4d35c..3c01ca880bd 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -2,6 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, Path, PathKind, PathSegment, UnresolvedType}, + parser::ParserErrorReason, token::{Keyword, TokenKind}, }; @@ -101,13 +102,25 @@ impl<'a> Parser<'a> { } let mut generics = Vec::new(); + let mut trailing_comma = false; + if self.eat_greater() { // TODO: error } else { loop { + let star_span = self.current_token_span; let typ = self.parse_type(); + if self.current_token_span == star_span { + self.eat_greater(); + break; + } + + if !trailing_comma && !generics.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, star_span); + } + generics.push(typ); - self.eat_commas(); + trailing_comma = self.eat_commas(); if self.eat_greater() { break; From 48a15a9b9f7f8c5c400ed601d039008c75dc17d6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 17:13:25 -0300 Subject: [PATCH 090/229] Some adjustments --- .../src/parser/parser/expression.rs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 5f7e059907c..a4326a41217 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -13,22 +13,16 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_expression(&mut self) -> Expression { - let start_span = self.current_token_span; - let kind = self.parse_term_kind(); - let span = self.span_since(start_span); - Expression { kind, span } + self.parse_term() } fn parse_term(&mut self) -> Expression { let start_span = self.current_token_span; - let kind = self.parse_term_kind(); - Expression { kind, span: self.span_since(start_span) } - } - - fn parse_term_kind(&mut self) -> ExpressionKind { if let Some(operator) = self.parse_unary_op() { let rhs = self.parse_term(); - return ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); + let kind = ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); + let span = self.span_since(start_span); + return Expression { kind, span }; } self.parse_atom_or_unary_right() @@ -52,7 +46,7 @@ impl<'a> Parser<'a> { } } - fn parse_atom_or_unary_right(&mut self) -> ExpressionKind { + fn parse_atom_or_unary_right(&mut self) -> Expression { let start_span = self.current_token_span; let mut atom = self.parse_atom(); @@ -81,8 +75,11 @@ impl<'a> Parser<'a> { } else if let Some(int) = self.eat_int() { Ident::new(int.to_string(), self.previous_token_span) } else { - // TODO: error - Ident::default() + self.push_error( + ParserErrorReason::ExpectedIdentifierAfterDot, + self.current_token_span, + ); + continue; }; let generics = if self.eat_double_colon() { @@ -126,7 +123,7 @@ impl<'a> Parser<'a> { break; } - atom.kind + atom } fn parse_atom(&mut self) -> Expression { From f042540e14fc4c3c70c99a8ddb360ed19739207d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 17:23:56 -0300 Subject: [PATCH 091/229] Parse constructors --- compiler/noirc_frontend/src/parser/errors.rs | 2 + .../src/parser/parser/expression.rs | 88 ++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 5edbab4ed48..bbc54e01021 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -33,6 +33,8 @@ pub enum ParserErrorReason { MissingPlusSeparatingTraitBounds, #[error("Expected a `,` separating these two arguments")] MissingCommaSeparatingArguments, + #[error("Expected a `,` separating these two constructor fields")] + MissingCommaSeparatingConstructorFields, #[error("Expected an identifier after `fn`")] ExpectedIdentifierAfterFn, #[error("Missing type for function parameter")] diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a4326a41217..4d792ed70dc 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,7 +1,8 @@ use crate::{ ast::{ - ArrayLiteral, BlockExpression, CallExpression, Expression, ExpressionKind, Ident, Literal, - MemberAccessExpression, MethodCallExpression, PrefixExpression, UnaryOp, + ArrayLiteral, BlockExpression, CallExpression, ConstructorExpression, Expression, + ExpressionKind, Ident, Literal, MemberAccessExpression, MethodCallExpression, Path, + PrefixExpression, UnaryOp, UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -152,12 +153,57 @@ impl<'a> Parser<'a> { let path = self.parse_path(); if !path.is_empty() { + if self.eat_left_brace() { + return self.parse_constructor(path); + } + return ExpressionKind::Variable(path); } ExpressionKind::Error } + fn parse_constructor(&mut self, path: Path) -> ExpressionKind { + let mut fields = Vec::new(); + let mut trailing_comma = false; + + loop { + let start_span = self.current_token_span; + let Some(ident) = self.eat_ident() else { + if !self.eat_left_brace() { + // TODO: error + } + break; + }; + + if !trailing_comma && !fields.is_empty() { + self.push_error( + ParserErrorReason::MissingCommaSeparatingConstructorFields, + start_span, + ); + } + + if self.eat_colon() { + let expression = self.parse_expression(); + fields.push((ident, expression)); + } else { + fields.push((ident.clone(), ident.into())); + } + + trailing_comma = self.eat_commas(); + + if self.eat_left_brace() { + break; + } + } + + ExpressionKind::Constructor(Box::new(ConstructorExpression { + typ: UnresolvedType::from_path(path), + fields, + struct_type: None, + })) + } + fn parse_literal(&mut self) -> Option { if let Some(bool) = self.eat_bool() { return Some(ExpressionKind::Literal(Literal::Bool(bool))); @@ -840,4 +886,42 @@ mod tests { assert_eq!(method_call.arguments.len(), 2); assert!(method_call.generics.is_none()); } + + #[test] + fn parses_empty_constructor() { + let src = "Foo {}"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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"); + } } From f51b5cc030713afb49af8ed62c7fc79f130734cb Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 17:26:45 -0300 Subject: [PATCH 092/229] Apparently `struct Foo;` is valid syntax --- .../src/parser/parser/structs.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 45a8faa1f8c..43392422e74 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -29,6 +29,10 @@ impl<'a> Parser<'a> { let generics = self.parse_generics(); + if self.eat_semicolons() { + return self.empty_struct(name, attributes, visibility, generics, start_span); + } + if !self.eat_left_brace() { // TODO: error return self.empty_struct(name, attributes, visibility, generics, start_span); @@ -117,6 +121,21 @@ mod tests { assert!(noir_struct.generics.is_empty()); } + #[test] + fn parse_empty_struct_followed_by_semicolon() { + let src = "struct Foo;"; + let (module, errors) = parse_program(src); + assert!(errors.is_empty()); + 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!(noir_struct.fields.is_empty()); + assert!(noir_struct.generics.is_empty()); + } + #[test] fn parse_empty_struct_with_generics() { let src = "struct Foo {}"; From 29e8137a890aeddd1991619e7cad1d9fb351eec2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 17:44:07 -0300 Subject: [PATCH 093/229] Parse type expression in generic type args --- .../src/parser/parser/expression.rs | 5 +-- .../src/parser/parser/generics.rs | 31 +++++++++++++++++-- .../src/parser/parser/type_expression.rs | 15 +++------ .../noirc_frontend/src/parser/parser/types.rs | 22 +++++++------ 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 4d792ed70dc..a580d8c26a6 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -170,7 +170,7 @@ impl<'a> Parser<'a> { loop { let start_span = self.current_token_span; let Some(ident) = self.eat_ident() else { - if !self.eat_left_brace() { + if !self.eat_right_brace() { // TODO: error } break; @@ -192,7 +192,7 @@ impl<'a> Parser<'a> { trailing_comma = self.eat_commas(); - if self.eat_left_brace() { + if self.eat_right_brace() { break; } } @@ -905,6 +905,7 @@ mod tests { let src = "Foo { x: 1, y, z: 2 }"; let mut parser = Parser::for_str(src); let expr = parser.parse_expression(); + assert_eq!(expr.span.end() as usize, src.len()); assert!(parser.errors.is_empty()); let ExpressionKind::Constructor(mut constructor) = expr.kind else { panic!("Expected constructor"); diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index ac7f38093ac..21d4ec38fff 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -118,13 +118,27 @@ impl<'a> Parser<'a> { } } else { let typ = self.parse_type(); - if self.current_token_span == start_span { + let typ = if self.current_token_span == start_span { + if let Ok(type_expr) = self.parse_type_expression() { + let span = type_expr.span(); + Some(UnresolvedType { + typ: UnresolvedTypeData::Expression(type_expr), + span, + }) + } else { + None + } + } else { + Some(typ) + }; + + let Some(typ) = typ else { + // TODO: error? (not sure if this is `<>` so test that) self.eat_greater(); break; - } + }; if !trailing_comma && !generic_type_args.is_empty() { - println!("1"); self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); } @@ -222,6 +236,17 @@ mod tests { assert_eq!(generics.named_args.len(), 0); } + #[test] + fn parses_generic_type_arg_that_is_an_int() { + let src = "<1>"; + let mut parser = Parser::for_str(src); + let generics = parser.parse_generic_type_args(); + assert!(parser.errors.is_empty()); + 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_not_integer() { let src = " diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 8a70c00b16d..12559fc39ad 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,18 +1,13 @@ -use crate::ast::UnresolvedTypeExpression; +use crate::{ast::UnresolvedTypeExpression, parser::ParserError}; use super::Parser; impl<'a> Parser<'a> { - pub(crate) fn parse_type_expression(&mut self) -> Option { + pub(crate) fn parse_type_expression( + &mut self, + ) -> Result { let expr = self.parse_expression(); let span = expr.span; - let type_expr = UnresolvedTypeExpression::from_expr(expr, span); - match type_expr { - Ok(type_expr) => Some(type_expr), - Err(parser_error) => { - self.errors.push(parser_error); - None - } - } + UnresolvedTypeExpression::from_expr(expr, span) } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index d3916ecf906..95fba817aef 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -103,17 +103,21 @@ impl<'a> Parser<'a> { let typ = self.parse_type(); if self.eat_semicolon() { - if let Some(expr) = self.parse_type_expression() { - if !self.eat_right_bracket() { - self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); - } - Some(UnresolvedTypeData::Array(expr, Box::new(typ))) - } else { - if !self.eat_right_bracket() { - self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); + match self.parse_type_expression() { + Ok(expr) => { + if !self.eat_right_bracket() { + self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); + } + Some(UnresolvedTypeData::Array(expr, Box::new(typ))) } + Err(error) => { + self.errors.push(error); + if !self.eat_right_bracket() { + self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); + } - Some(UnresolvedTypeData::Slice(Box::new(typ))) + Some(UnresolvedTypeData::Slice(Box::new(typ))) + } } } else { if !self.eat_right_bracket() { From e888d5a77f2fe46518827d918f2f4423ab6f7ce5 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 21:39:28 -0300 Subject: [PATCH 094/229] Parse some primitive types --- compiler/noirc_frontend/src/hir_def/types.rs | 2 +- compiler/noirc_frontend/src/parser/errors.rs | 4 + .../noirc_frontend/src/parser/parser/types.rs | 138 ++++++++++++++++-- 3 files changed, 132 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index c170d2cc08f..5f83c3c2bb6 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -192,7 +192,7 @@ impl std::fmt::Display for Kind { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord, strum_macros::EnumIter)] pub enum QuotedType { Expr, Quoted, diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index bbc54e01021..e4e1dc6ae95 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -65,6 +65,10 @@ pub enum ParserErrorReason { ExpectedBracketAfterArray, #[error("Expected `]` after slice")] ExpectedBracketAfterSlice, + #[error("Expected `>` after str type length")] + ExpectedGreaterAfterStringTypeLength, + #[error("Expected str type length after `str`")] + ExpectedStringTypeLength, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 95fba817aef..0405fd219ef 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,9 +1,10 @@ use noirc_errors::Span; use crate::{ - ast::{Ident, UnresolvedType, UnresolvedTypeData}, + ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::ParserErrorReason, token::{Keyword, Token}, + QuotedType, }; use super::Parser; @@ -19,19 +20,11 @@ impl<'a> Parser<'a> { } fn parse_unresolved_type_data(&mut self) -> UnresolvedTypeData { - if let Some(typ) = self.parse_parentheses_type() { - return typ; - } - - if let Some(typ) = self.parse_bool_type() { - return typ; - } - - if let Some(typ) = self.parse_field_type() { + if let Some(typ) = self.parse_primitive_type() { return typ; } - if let Some(typ) = self.parse_int_type() { + if let Some(typ) = self.parse_parentheses_type() { return typ; } @@ -54,6 +47,30 @@ impl<'a> Parser<'a> { UnresolvedTypeData::Error } + fn parse_primitive_type(&mut self) -> Option { + if let Some(typ) = self.parse_field_type() { + return Some(typ); + } + + if let Some(typ) = self.parse_int_type() { + return Some(typ); + } + + if let Some(typ) = self.parse_bool_type() { + return Some(typ); + } + + if let Some(typ) = self.parse_str_type() { + return Some(typ); + } + + if let Some(typ) = self.parse_comptime_type() { + return Some(typ); + } + + None + } + fn parse_bool_type(&mut self) -> Option { if self.eat_keyword(Keyword::Bool) { return Some(UnresolvedTypeData::Bool); @@ -84,6 +101,78 @@ impl<'a> Parser<'a> { None } + fn parse_str_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::String) { + return None; + } + + if !self.eat_less() { + self.push_error(ParserErrorReason::ExpectedStringTypeLength, self.current_token_span); + let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); + return Some(UnresolvedTypeData::String(expr)); + } + + let expr = match self.parse_type_expression() { + Ok(expr) => expr, + Err(error) => { + self.errors.push(error); + UnresolvedTypeExpression::Constant(0, self.current_token_span) + } + }; + + if !self.eat_greater() { + self.push_error( + ParserErrorReason::ExpectedGreaterAfterStringTypeLength, + self.current_token_span, + ); + } + + Some(UnresolvedTypeData::String(expr)) + } + + 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 + } + fn parses_mutable_reference_type(&mut self) -> Option { if self.eat(Token::Ampersand) { if !self.eat_keyword(Keyword::Mut) { @@ -184,12 +273,15 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { + use strum::IntoEnumIterator; + use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, }, + QuotedType, }; #[test] @@ -231,6 +323,30 @@ mod tests { assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } + #[test] + fn parses_str_type() { + let src = "str<10>"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeData::String(expr) = typ.typ else { panic!("Expected a string type") }; + assert_eq!(expr.to_string(), "10"); + } + + #[test] + fn parses_comptime_types() { + for quoted_type in QuotedType::iter() { + let src = quoted_type.to_string(); + let mut parser = Parser::for_str(&src); + let typ = parser.parse_type(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeData::Quoted(parsed_qouted_type) = typ.typ else { + panic!("Expected a quoted type for {}", quoted_type.to_string()) + }; + assert_eq!(parsed_qouted_type, quoted_type); + } + } + #[test] fn parses_tuple_type() { let src = "(Field, bool)"; From 1c4592ed2732d94888cbcdd9564c152f01fe59e6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 21:51:25 -0300 Subject: [PATCH 095/229] Parse interned and resolved types --- .../noirc_frontend/src/parser/parser/types.rs | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 0405fd219ef..49e6ecd1184 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -3,7 +3,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::ParserErrorReason, - token::{Keyword, Token}, + token::{Keyword, Token, TokenKind}, QuotedType, }; @@ -68,6 +68,14 @@ impl<'a> Parser<'a> { return Some(typ); } + if let Some(typ) = self.parse_resolved_type() { + return Some(typ); + } + + if let Some(typ) = self.parse_interned_type() { + return Some(typ); + } + None } @@ -173,6 +181,32 @@ impl<'a> Parser<'a> { None } + 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 + } + + 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) { if !self.eat_keyword(Keyword::Mut) { From 028a6db169fe41214f2d1b1e87be06dcc131b4b4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 21:51:32 -0300 Subject: [PATCH 096/229] Refactor --- .../src/parser/parser/generics.rs | 69 ++++++++++++------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 21d4ec38fff..6a3c01d5fc8 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -49,40 +49,61 @@ impl<'a> Parser<'a> { } fn parse_generic(&mut self) -> Option { - // Check `T` + 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 + } + + fn parse_variable_generic(&mut self) -> Option { if let Some(ident) = self.eat_ident() { return Some(UnresolvedGeneric::Variable(ident)); } - // Check `let N: u32` - if self.eat_keyword(Keyword::Let) { - let Some(ident) = self.eat_ident() else { - return None; - }; + None + } - if !self.eat_colon() { - self.push_error( - ParserErrorReason::MissingTypeForNumericGeneric, - self.current_token_span, - ); - return Some(UnresolvedGeneric::Numeric { ident, typ: type_u32() }); - } + fn parse_numeric_generic(&mut self) -> Option { + if !self.eat_keyword(Keyword::Let) { + return None; + } - let typ = self.parse_type(); - 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); - } - } else { + let Some(ident) = self.eat_ident() else { + return None; + }; + + if !self.eat_colon() { + self.push_error( + ParserErrorReason::MissingTypeForNumericGeneric, + self.current_token_span, + ); + return Some(UnresolvedGeneric::Numeric { ident, typ: type_u32() }); + } + + let typ = self.parse_type(); + 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); } - - return Some(UnresolvedGeneric::Numeric { ident, typ }); + } else { + self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); } - // Check resolved generics + Some(UnresolvedGeneric::Numeric { ident, typ }) + } + + fn parse_resolved_generic(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::QuotedType) { match token.into_token() { Token::QuotedType(id) => { From 7668331672b190b552dc49bad8f5f0217c63b5ac Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 22:19:31 -0300 Subject: [PATCH 097/229] Parse if expression --- compiler/noirc_frontend/src/parser/errors.rs | 2 + .../src/parser/parser/expression.rs | 178 +++++++++++++++--- .../src/parser/parser/generics.rs | 15 -- 3 files changed, 156 insertions(+), 39 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index e4e1dc6ae95..66ef4899a00 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -69,6 +69,8 @@ pub enum ParserErrorReason { ExpectedGreaterAfterStringTypeLength, #[error("Expected str type length after `str`")] ExpectedStringTypeLength, + #[error("Expected `{{` or `if` after `else`")] + ExpectedLeftBraceOfIfAfterElse, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a580d8c26a6..09fe48a39f3 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,8 +1,10 @@ +use noirc_errors::Span; + use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, ConstructorExpression, Expression, - ExpressionKind, Ident, Literal, MemberAccessExpression, MethodCallExpression, Path, - PrefixExpression, UnaryOp, UnresolvedType, + ExpressionKind, Ident, IfExpression, Literal, MemberAccessExpression, MethodCallExpression, + Path, PrefixExpression, UnaryOp, UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -14,19 +16,25 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_expression(&mut self) -> Expression { - self.parse_term() + self.parse_term(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. + pub(crate) fn parse_expression_no_constructors(&mut self) -> Expression { + self.parse_term(false) // allow constructors } - fn parse_term(&mut self) -> Expression { + fn parse_term(&mut self, allow_constructos: bool) -> Expression { let start_span = self.current_token_span; if let Some(operator) = self.parse_unary_op() { - let rhs = self.parse_term(); + let rhs = self.parse_term(allow_constructos); let kind = ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); let span = self.span_since(start_span); return Expression { kind, span }; } - self.parse_atom_or_unary_right() + self.parse_atom_or_unary_right(allow_constructos) } fn parse_unary_op(&mut self) -> Option { @@ -47,9 +55,9 @@ impl<'a> Parser<'a> { } } - fn parse_atom_or_unary_right(&mut self) -> Expression { + fn parse_atom_or_unary_right(&mut self, allow_constructos: bool) -> Expression { let start_span = self.current_token_span; - let mut atom = self.parse_atom(); + let mut atom = self.parse_atom(allow_constructos); loop { let is_macro_call = @@ -127,13 +135,13 @@ impl<'a> Parser<'a> { atom } - fn parse_atom(&mut self) -> Expression { + fn parse_atom(&mut self, allow_constructos: bool) -> Expression { let start_span = self.current_token_span; - let kind = self.parse_atom_kind(); + let kind = self.parse_atom_kind(allow_constructos); Expression { kind, span: self.span_since(start_span) } } - fn parse_atom_kind(&mut self) -> ExpressionKind { + fn parse_atom_kind(&mut self, allow_constructos: bool) -> ExpressionKind { if let Some(literal) = self.parse_literal() { return literal; } @@ -142,27 +150,47 @@ impl<'a> Parser<'a> { return kind; } - if self.eat_keyword(Keyword::Unsafe) { - let start_span = self.span_since(self.previous_token_span); - if let Some(block) = self.parse_block_expression() { - return ExpressionKind::Unsafe(block, self.span_since(start_span)); - } else { - return ExpressionKind::Error; - }; + if let Some(kind) = self.parse_unsafe_expr() { + return kind; } - let path = self.parse_path(); - if !path.is_empty() { - if self.eat_left_brace() { - return self.parse_constructor(path); - } + if let Some(kind) = self.parse_path_expr(allow_constructos) { + return kind; + } - return ExpressionKind::Variable(path); + if let Some(kind) = self.parse_if_expr() { + return kind; } ExpressionKind::Error } + fn parse_unsafe_expr(&mut self) -> Option { + if !self.eat_keyword(Keyword::Unsafe) { + return None; + } + + let start_span = self.span_since(self.previous_token_span); + if let Some(block) = self.parse_block_expression() { + Some(ExpressionKind::Unsafe(block, self.span_since(start_span))) + } else { + Some(ExpressionKind::Error) + } + } + + fn parse_path_expr(&mut self, allow_constructos: bool) -> Option { + let path = self.parse_path(); + if path.is_empty() { + return None; + } + + if allow_constructos && self.eat_left_brace() { + return Some(self.parse_constructor(path)); + } + + Some(ExpressionKind::Variable(path)) + } + fn parse_constructor(&mut self, path: Path) -> ExpressionKind { let mut fields = Vec::new(); let mut trailing_comma = false; @@ -204,6 +232,50 @@ impl<'a> Parser<'a> { })) } + fn parse_if_expr(&mut self) -> Option { + if !self.eat_keyword(Keyword::If) { + return None; + } + + let condition = self.parse_expression_no_constructors(); + + let start_span = self.current_token_span; + let Some(consequence) = self.parse_block_expression() else { + self.push_error( + ParserErrorReason::ExpectedLeftBraceAfterIfCondition, + self.current_token_span, + ); + let span = Span::from(self.previous_token_span.end()..self.previous_token_span.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(alternative) = self.parse_block_expression() { + let span = self.span_since(start_span); + Some(Expression { kind: ExpressionKind::Block(alternative), span }) + } else if let Some(if_expr) = self.parse_if_expr() { + Some(Expression { kind: if_expr, span: self.span_since(start_span) }) + } else { + self.push_error( + ParserErrorReason::ExpectedLeftBraceOfIfAfterElse, + self.current_token_span, + ); + None + } + } else { + None + }; + + Some(ExpressionKind::If(Box::new(IfExpression { condition, consequence, alternative }))) + } + fn parse_literal(&mut self) -> Option { if let Some(bool) = self.eat_bool() { return Some(ExpressionKind::Literal(Literal::Bool(bool))); @@ -925,4 +997,62 @@ mod tests { assert_eq!(name.to_string(), "z"); assert_eq!(expr.to_string(), "2"); } + + #[test] + fn parses_parses_if_true() { + let src = "if true { 1 }"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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"); + }; + } } diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 6a3c01d5fc8..19f9b7cf85f 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -96,8 +96,6 @@ impl<'a> Parser<'a> { { self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); } - } else { - self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); } Some(UnresolvedGeneric::Numeric { ident, typ }) @@ -268,19 +266,6 @@ mod tests { assert_eq!(generics.ordered_args[0].to_string(), "1"); } - #[test] - fn parse_numeric_generic_error_if_not_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(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); - } - #[test] fn parse_numeric_generic_error_if_invalid_integer() { let src = " From 7e658dd93ac938020fce8261a1ef9867c07a5017 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 22:32:07 -0300 Subject: [PATCH 098/229] Parse comptime block statement --- .../src/parser/parser/statement.rs | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 93313c0b079..6b0d3406497 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -37,7 +37,8 @@ impl<'a> Parser<'a> { return StatementKind::Continue; } - if let Some(let_statement) = self.parse_let_statement(attributes) { + if self.token.token() == &Token::Keyword(Keyword::Let) { + let let_statement = self.parse_let_statement(attributes).unwrap(); return StatementKind::Let(let_statement); } @@ -45,9 +46,36 @@ impl<'a> Parser<'a> { return StatementKind::Constrain(constrain); } + if self.token.token() == &Token::Keyword(Keyword::Comptime) { + return self.parse_comptime_statement(attributes).unwrap(); + } + StatementKind::Expression(self.parse_expression()) } + fn parse_comptime_statement( + &mut self, + attributes: Vec<(Attribute, Span)>, + ) -> Option { + if !self.eat_keyword(Keyword::Comptime) { + return None; + } + + let start_span = self.current_token_span; + if let Some(block) = self.parse_block_expression() { + let span = self.span_since(start_span); + return Some(StatementKind::Comptime(Box::new(Statement { + kind: StatementKind::Expression(Expression::new( + ExpressionKind::Block(block), + span, + )), + span, + }))); + } + + None + } + fn parse_let_statement(&mut self, attributes: Vec<(Attribute, Span)>) -> Option { if !self.eat_keyword(Keyword::Let) { return None; @@ -110,8 +138,10 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { + use acvm::acir::native_types::Expression; + use crate::{ - ast::{ConstrainKind, StatementKind, UnresolvedTypeData}, + ast::{ConstrainKind, ExpressionKind, StatementKind, UnresolvedTypeData}, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, @@ -210,4 +240,22 @@ mod tests { let reason = get_single_error(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ConstrainDeprecated)); } + + #[test] + fn parses_comptime_block() { + let src = "comptime { 1 }"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + 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); + } } From 90cd320315517baa23f69f68b96e8b984f1d3709 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 27 Sep 2024 22:36:41 -0300 Subject: [PATCH 099/229] Parse comptime let --- .../src/parser/parser/statement.rs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 6b0d3406497..7c90ccd7580 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -62,6 +62,7 @@ impl<'a> Parser<'a> { } let start_span = self.current_token_span; + if let Some(block) = self.parse_block_expression() { let span = self.span_since(start_span); return Some(StatementKind::Comptime(Box::new(Statement { @@ -73,6 +74,13 @@ impl<'a> Parser<'a> { }))); } + if let Some(let_statement) = self.parse_let_statement(attributes) { + return Some(StatementKind::Comptime(Box::new(Statement { + kind: StatementKind::Let(let_statement), + span: self.span_since(start_span), + }))); + } + None } @@ -138,8 +146,6 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use acvm::acir::native_types::Expression; - use crate::{ ast::{ConstrainKind, ExpressionKind, StatementKind, UnresolvedTypeData}, parser::{ @@ -258,4 +264,18 @@ mod tests { }; assert_eq!(block.statements.len(), 1); } + + #[test] + fn parses_comptime_let() { + let src = "comptime let x = 1;"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement(); + assert!(parser.errors.is_empty()); + let StatementKind::Comptime(statement) = statement.kind else { + panic!("Expected comptime statement"); + }; + let StatementKind::Let(..) = statement.kind else { + panic!("Expected let statement"); + }; + } } From 1cd28c1365f45dab7cff7e804f67ff5018a3eff4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 07:49:36 -0300 Subject: [PATCH 100/229] Parse cast --- .../src/parser/parser/expression.rs | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 09fe48a39f3..99359dacb0d 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -2,9 +2,9 @@ use noirc_errors::Span; use crate::{ ast::{ - ArrayLiteral, BlockExpression, CallExpression, ConstructorExpression, Expression, - ExpressionKind, Ident, IfExpression, Literal, MemberAccessExpression, MethodCallExpression, - Path, PrefixExpression, UnaryOp, UnresolvedType, + ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, + Expression, ExpressionKind, Ident, IfExpression, Literal, MemberAccessExpression, + MethodCallExpression, Path, PrefixExpression, UnaryOp, UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -129,6 +129,15 @@ impl<'a> Parser<'a> { continue; } + if self.eat_keyword(Keyword::As) { + let typ = self.parse_type(); + let kind = + ExpressionKind::Cast(Box::new(CastExpression { lhs: atom, r#type: typ })); + let span = self.span_since(start_span); + atom = Expression { kind, span }; + continue; + } + break; } @@ -1055,4 +1064,17 @@ mod tests { panic!("Expected if"); }; } + + #[test] + fn parses_cast() { + let src = "1 as u8"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression(); + assert!(parser.errors.is_empty()); + 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"); + } } From 57b38bfe62484989f59acfbb3dd87bcd5049fb3f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 07:57:43 -0300 Subject: [PATCH 101/229] Handle missing type after `as` --- compiler/noirc_frontend/src/parser/errors.rs | 2 ++ .../src/parser/parser/expression.rs | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 66ef4899a00..928e92ec1e1 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -71,6 +71,8 @@ pub enum ParserErrorReason { ExpectedStringTypeLength, #[error("Expected `{{` or `if` after `else`")] ExpectedLeftBraceOfIfAfterElse, + #[error("Expected type after `as`")] + ExpectedTypeAfterAs, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 99359dacb0d..0f0b4ed5f11 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -4,7 +4,7 @@ use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, Literal, MemberAccessExpression, - MethodCallExpression, Path, PrefixExpression, UnaryOp, UnresolvedType, + MethodCallExpression, Path, PrefixExpression, UnaryOp, UnresolvedType, UnresolvedTypeData, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -131,6 +131,13 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::As) { let typ = self.parse_type(); + if let UnresolvedTypeData::Error = typ.typ { + self.push_error( + ParserErrorReason::ExpectedTypeAfterAs, + self.previous_token_span, + ); + } + let kind = ExpressionKind::Cast(Box::new(CastExpression { lhs: atom, r#type: typ })); let span = self.span_since(start_span); @@ -1077,4 +1084,17 @@ mod tests { 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 reason = get_single_error(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::ExpectedTypeAfterAs)); + } } From 2c687e1c0f145b13bce65a0cfa6752908da5fdef Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 08:10:25 -0300 Subject: [PATCH 102/229] Better way to parse types --- .../src/hir/comptime/interpreter/builtin.rs | 3 +- compiler/noirc_frontend/src/parser/errors.rs | 4 +- .../src/parser/parser/expression.rs | 13 +--- .../src/parser/parser/function.rs | 4 +- .../src/parser/parser/generics.rs | 10 +-- .../noirc_frontend/src/parser/parser/impls.rs | 8 +- .../noirc_frontend/src/parser/parser/path.rs | 5 +- .../src/parser/parser/structs.rs | 2 +- .../src/parser/parser/traits.rs | 2 +- .../src/parser/parser/type_alias.rs | 2 +- .../noirc_frontend/src/parser/parser/types.rs | 76 +++++++++++-------- .../src/parser/parser/where_clause.rs | 5 +- 12 files changed, 67 insertions(+), 67 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 0b0658920ab..dc90e6da79f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -748,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)) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 928e92ec1e1..b8df1ec60ed 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -71,8 +71,8 @@ pub enum ParserErrorReason { ExpectedStringTypeLength, #[error("Expected `{{` or `if` after `else`")] ExpectedLeftBraceOfIfAfterElse, - #[error("Expected type after `as`")] - ExpectedTypeAfterAs, + #[error("Expected type after this")] + ExpectedTypeAfterThis, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 0f0b4ed5f11..e227a371333 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -4,7 +4,7 @@ use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, Literal, MemberAccessExpression, - MethodCallExpression, Path, PrefixExpression, UnaryOp, UnresolvedType, UnresolvedTypeData, + MethodCallExpression, Path, PrefixExpression, UnaryOp, UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -130,14 +130,7 @@ impl<'a> Parser<'a> { } if self.eat_keyword(Keyword::As) { - let typ = self.parse_type(); - if let UnresolvedTypeData::Error = typ.typ { - self.push_error( - ParserErrorReason::ExpectedTypeAfterAs, - self.previous_token_span, - ); - } - + 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); @@ -1095,6 +1088,6 @@ mod tests { let mut parser = Parser::for_str(&src); parser.parse_expression(); let reason = get_single_error(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ExpectedTypeAfterAs)); + assert!(matches!(reason, ParserErrorReason::ExpectedTypeAfterThis)); } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index b8ada4114d0..d67c6ae9286 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -89,7 +89,7 @@ impl<'a> Parser<'a> { let (return_type, return_visibility) = if self.eat(Token::Arrow) { let visibility = self.parse_visibility(); - (FunctionReturnType::Ty(self.parse_type()), visibility) + (FunctionReturnType::Ty(self.parse_type_or_error()), visibility) } else { ( FunctionReturnType::Default(Span::from( @@ -166,7 +166,7 @@ impl<'a> Parser<'a> { if self.eat_colon() { let visibility = self.parse_visibility(); - let typ = self.parse_type(); + let typ = self.parse_type_or_error(); parameters.push(Param { visibility, pattern, diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 19f9b7cf85f..fc41aa604fd 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -89,7 +89,7 @@ impl<'a> Parser<'a> { return Some(UnresolvedGeneric::Numeric { ident, typ: type_u32() }); } - let typ = self.parse_type(); + 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) @@ -129,7 +129,7 @@ impl<'a> Parser<'a> { } if self.eat_assign() { - let typ = self.parse_type(); + let typ = self.parse_type_or_error(); generic_type_args.named_args.push((ident, typ)); } else { let typ = self.parse_path_type_after_ident(ident); @@ -137,7 +137,7 @@ impl<'a> Parser<'a> { } } else { let typ = self.parse_type(); - let typ = if self.current_token_span == start_span { + let typ = typ.or_else(|| { if let Ok(type_expr) = self.parse_type_expression() { let span = type_expr.span(); Some(UnresolvedType { @@ -147,9 +147,7 @@ impl<'a> Parser<'a> { } else { None } - } else { - Some(typ) - }; + }); let Some(typ) = typ else { // TODO: error? (not sure if this is `<>` so test that) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 3f8ec2662e3..c684d95e3bb 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -22,7 +22,7 @@ impl<'a> Parser<'a> { let generics = self.parse_generics(); let type_span_start = self.current_token_span; - let object_type = self.parse_type(); + let object_type = self.parse_type_or_error(); let type_span = self.span_since(type_span_start); if self.eat_keyword(Keyword::For) { @@ -93,7 +93,7 @@ impl<'a> Parser<'a> { trait_generics: GenericTypeArgs, trait_name: Path, ) -> NoirTraitImpl { - let object_type = self.parse_type(); + let object_type = self.parse_type_or_error(); let where_clause = self.parse_where_clause(); let items = self.parse_trait_impl_items(); @@ -197,7 +197,7 @@ impl<'a> Parser<'a> { }; let alias = if self.eat_assign() { - self.parse_type() + self.parse_type_or_error() } else { UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() } }; @@ -221,7 +221,7 @@ impl<'a> Parser<'a> { }; let typ = if self.eat_colon() { - self.parse_type() + self.parse_type_or_error() } else { UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } }; diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 3c01ca880bd..6b55e643440 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -109,11 +109,10 @@ impl<'a> Parser<'a> { } else { loop { let star_span = self.current_token_span; - let typ = self.parse_type(); - if self.current_token_span == star_span { + let Some(typ) = self.parse_type() else { self.eat_greater(); break; - } + }; if !trailing_comma && !generics.is_empty() { self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, star_span); diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 43392422e74..8a4889167dc 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -52,7 +52,7 @@ impl<'a> Parser<'a> { // TODO: error } - let typ = self.parse_type(); + let typ = self.parse_type_or_error(); fields.push(Documented::new(StructField { name, typ }, doc_comments)); diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index c1d193e5f12..ce7ae4bb9b5 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -118,7 +118,7 @@ impl<'a> Parser<'a> { }; let typ = if self.eat_colon() { - self.parse_type() + self.parse_type_or_error() } else { // TODO: error UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index e551b865544..4de8c27ee54 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -30,7 +30,7 @@ impl<'a> Parser<'a> { }; } - let typ = self.parse_type(); + let typ = self.parse_type_or_error(); NoirTypeAlias { name, generics, typ, span: self.span_since(start_span) } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 49e6ecd1184..75c3fcf888b 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -10,41 +10,51 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - pub(crate) fn parse_type(&mut self) -> UnresolvedType { - let start_span = self.current_token_span; + pub(crate) fn parse_type_or_error(&mut self) -> UnresolvedType { + if let Some(typ) = self.parse_type() { + typ + } else { + self.push_error(ParserErrorReason::ExpectedTypeAfterThis, self.previous_token_span); + UnresolvedType { + typ: UnresolvedTypeData::Error, + span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), + } + } + } - let typ = self.parse_unresolved_type_data(); + 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); - - UnresolvedType { typ, span } + Some(UnresolvedType { typ, span }) } - fn parse_unresolved_type_data(&mut self) -> UnresolvedTypeData { + fn parse_unresolved_type_data(&mut self) -> Option { if let Some(typ) = self.parse_primitive_type() { - return typ; + return Some(typ); } if let Some(typ) = self.parse_parentheses_type() { - return typ; + return Some(typ); } if let Some(typ) = self.parse_array_or_slice_type() { - return typ; + return Some(typ); } if let Some(typ) = self.parses_mutable_reference_type() { - return typ; + return Some(typ); } let path = self.parse_path_no_turbofish(); if !path.is_empty() { let generics = self.parse_generic_type_args(); - return UnresolvedTypeData::Named(path, generics, false); + return Some(UnresolvedTypeData::Named(path, generics, false)); } // TODO: parse more types - UnresolvedTypeData::Error + None } fn parse_primitive_type(&mut self) -> Option { @@ -212,7 +222,9 @@ impl<'a> Parser<'a> { if !self.eat_keyword(Keyword::Mut) { // TODO: error } - return Some(UnresolvedTypeData::MutableReference(Box::new(self.parse_type()))); + return Some(UnresolvedTypeData::MutableReference(Box::new( + self.parse_type_or_error(), + ))); }; None @@ -223,7 +235,7 @@ impl<'a> Parser<'a> { return None; } - let typ = self.parse_type(); + let typ = self.parse_type_or_error(); if self.eat_semicolon() { match self.parse_type_expression() { @@ -263,13 +275,11 @@ impl<'a> Parser<'a> { let mut types = Vec::new(); let mut trailing_comma = false; loop { - let start_span = self.current_token_span; - let typ = self.parse_type(); - if self.current_token_span == start_span { + let Some(typ) = self.parse_type() else { // TODO: error self.eat_right_paren(); break; - } + }; types.push(typ); @@ -298,7 +308,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { if self.eat_colon() { - self.parse_type() + self.parse_type_or_error() } else { UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } } @@ -322,7 +332,7 @@ mod tests { fn parses_unit_type() { let src = "()"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); assert!(matches!(typ.typ, UnresolvedTypeData::Unit)); } @@ -331,7 +341,7 @@ mod tests { fn parses_bool_type() { let src = "bool"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); } @@ -340,7 +350,7 @@ mod tests { fn parses_int_type() { let src = "u32"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); assert!(matches!( typ.typ, @@ -352,7 +362,7 @@ mod tests { fn parses_field_type() { let src = "Field"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } @@ -361,7 +371,7 @@ mod tests { fn parses_str_type() { let src = "str<10>"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::String(expr) = typ.typ else { panic!("Expected a string type") }; assert_eq!(expr.to_string(), "10"); @@ -372,7 +382,7 @@ mod tests { for quoted_type in QuotedType::iter() { let src = quoted_type.to_string(); let mut parser = Parser::for_str(&src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Quoted(parsed_qouted_type) = typ.typ else { panic!("Expected a quoted type for {}", quoted_type.to_string()) @@ -385,7 +395,7 @@ mod tests { fn parses_tuple_type() { let src = "(Field, bool)"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; assert_eq!(types.len(), 2); @@ -401,7 +411,7 @@ mod tests { fn parses_tuple_type_one_element() { let src = "(Field,)"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; assert_eq!(types.len(), 1); @@ -414,7 +424,7 @@ mod tests { fn parses_parenthesized_type() { let src = "(Field)"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") @@ -426,7 +436,7 @@ mod tests { fn parses_unclosed_parentheses_type() { let src = "(Field"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); // TODO: there should be an error here let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") @@ -438,7 +448,7 @@ mod tests { fn parses_mutable_reference_type() { let src = "&mut Field"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::MutableReference(typ) = typ.typ else { panic!("Expected a mutable reference type") @@ -450,7 +460,7 @@ mod tests { fn parses_named_type_no_generics() { let src = "foo::Bar"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { panic!("Expected a named type") @@ -463,7 +473,7 @@ mod tests { fn parses_slice_type() { let src = "[Field]"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Slice(typ) = typ.typ else { panic!("Expected a slice type") }; assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); @@ -486,7 +496,7 @@ mod tests { fn parses_array_type() { let src = "[Field; 10]"; let mut parser = Parser::for_str(src); - let typ = parser.parse_type(); + let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Array(expr, typ) = typ.typ else { panic!("Expected an array type") diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 19ebc031ab1..3b3957a0ce3 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -17,10 +17,9 @@ impl<'a> Parser<'a> { let mut trailing_comma = false; loop { let start_span = self.current_token_span; - let typ = self.parse_type(); - if self.current_token_span == start_span { + let Some(typ) = self.parse_type() else { break; - } + }; if !trailing_comma && !constraints.is_empty() { self.push_error(ParserErrorReason::MissingCommaSeparatingTraitBounds, start_span); From 490dc67f61112565bef40dbe67436426676b3e3a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 08:29:08 -0300 Subject: [PATCH 103/229] Better way to parse expressions --- .../noirc_frontend/src/elaborator/comptime.rs | 2 +- .../src/hir/comptime/interpreter/builtin.rs | 3 +- .../noirc_frontend/src/hir/comptime/value.rs | 2 +- compiler/noirc_frontend/src/parser/errors.rs | 4 +- .../noirc_frontend/src/parser/parser/call.rs | 5 +- .../src/parser/parser/expression.rs | 170 ++++++++++-------- .../src/parser/parser/global.rs | 2 +- .../noirc_frontend/src/parser/parser/impls.rs | 2 +- .../src/parser/parser/statement.rs | 6 +- .../src/parser/parser/traits.rs | 3 +- .../src/parser/parser/type_expression.rs | 12 +- 11 files changed, 125 insertions(+), 86 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 5bf4a9b0c36..e11e07422e4 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -263,7 +263,7 @@ impl<'context> Elaborator<'context> { } let parser = Parser::for_tokens(tokens); - let expression = parse_result(parser, Parser::parse_expression) + let expression = parse_result(parser, Parser::parse_expression_or_error) .map_err(|mut errors| (errors.swap_remove(0).into(), location.file))?; let (mut func, mut arguments) = match expression.kind { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index dc90e6da79f..6937efa97b6 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -678,7 +678,8 @@ fn quoted_as_expr( ) -> IResult { let argument = check_one_argument(arguments, location)?; - let result = parse(interner, argument.clone(), Parser::parse_expression, "an expression"); + 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))); } diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index d77e8b48b16..dccabd2f874 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -261,7 +261,7 @@ impl Value { tokens_to_parse.0.push(SpannedToken::new(Token::RightBrace, location.span)); let parser = Parser::for_tokens(tokens_to_parse); - return match parse_result(parser, Parser::parse_expression) { + return match parse_result(parser, Parser::parse_expression_or_error) { Ok(expr) => Ok(expr), Err(mut errors) => { let error = errors.swap_remove(0); diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index b8df1ec60ed..e959b460f73 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -13,8 +13,6 @@ use super::labels::ParsingRuleLabel; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum ParserErrorReason { - #[error("Expected expression")] - ExpectedExpression, #[error("Expected pattern")] ExpectedPattern, #[error("Unexpected `;`")] @@ -73,6 +71,8 @@ pub enum ParserErrorReason { ExpectedLeftBraceOfIfAfterElse, #[error("Expected type after this")] ExpectedTypeAfterThis, + #[error("Expected expression after this")] + ExpectedExpressionAfterThis, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/call.rs b/compiler/noirc_frontend/src/parser/parser/call.rs index 8dfdded9c22..a400b90ee6e 100644 --- a/compiler/noirc_frontend/src/parser/parser/call.rs +++ b/compiler/noirc_frontend/src/parser/parser/call.rs @@ -16,11 +16,10 @@ impl<'a> Parser<'a> { let mut trailing_comma = false; loop { let start_span = self.current_token_span; - let expr = self.parse_expression(); - if start_span == self.current_token_span { + let Some(expr) = self.parse_expression() else { self.eat_right_paren(); break; - } + }; if !trailing_comma && !arguments.is_empty() { self.push_error(ParserErrorReason::MissingCommaSeparatingArguments, start_span); diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index e227a371333..f379b332eb0 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -15,23 +15,52 @@ use super::Parser; // term -> atom_or_right_unary -> atom impl<'a> Parser<'a> { - pub(crate) fn parse_expression(&mut self) -> Expression { - self.parse_term(true) // allow constructors + pub(crate) fn parse_expression_or_error(&mut self) -> Expression { + self.parse_expression_or_error_impl(true) // allow constructors + } + + 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. - pub(crate) fn parse_expression_no_constructors(&mut self) -> Expression { - self.parse_term(false) // allow constructors + pub(crate) fn parse_expression_no_constructors_or_error(&mut self) -> Expression { + self.parse_expression_or_error_impl(false) // allow constructors } - fn parse_term(&mut self, allow_constructos: bool) -> Expression { + pub(crate) fn parse_expression_or_error_impl(&mut self, allow_constructos: bool) -> Expression { + if let Some(expr) = self.parse_expression_impl(allow_constructos) { + expr + } else { + self.push_error( + ParserErrorReason::ExpectedExpressionAfterThis, + self.previous_token_span, + ); + Expression { + kind: ExpressionKind::Error, + span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), + } + } + } + + fn parse_expression_impl(&mut self, allow_constructos: bool) -> Option { + self.parse_term(allow_constructos) + } + + fn parse_term(&mut self, allow_constructos: bool) -> Option { let start_span = self.current_token_span; if let Some(operator) = self.parse_unary_op() { - let rhs = self.parse_term(allow_constructos); + let Some(rhs) = self.parse_term(allow_constructos) else { + self.push_error( + ParserErrorReason::ExpectedExpressionAfterThis, + self.previous_token_span, + ); + return None; + }; let kind = ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); let span = self.span_since(start_span); - return Expression { kind, span }; + return Some(Expression { kind, span }); } self.parse_atom_or_unary_right(allow_constructos) @@ -55,9 +84,9 @@ impl<'a> Parser<'a> { } } - fn parse_atom_or_unary_right(&mut self, allow_constructos: bool) -> Expression { + fn parse_atom_or_unary_right(&mut self, allow_constructos: bool) -> Option { let start_span = self.current_token_span; - let mut atom = self.parse_atom(allow_constructos); + let mut atom = self.parse_atom(allow_constructos)?; loop { let is_macro_call = @@ -141,37 +170,37 @@ impl<'a> Parser<'a> { break; } - atom + Some(atom) } - fn parse_atom(&mut self, allow_constructos: bool) -> Expression { + fn parse_atom(&mut self, allow_constructos: bool) -> Option { let start_span = self.current_token_span; - let kind = self.parse_atom_kind(allow_constructos); - Expression { kind, span: self.span_since(start_span) } + let kind = self.parse_atom_kind(allow_constructos)?; + Some(Expression { kind, span: self.span_since(start_span) }) } - fn parse_atom_kind(&mut self, allow_constructos: bool) -> ExpressionKind { + fn parse_atom_kind(&mut self, allow_constructos: bool) -> Option { if let Some(literal) = self.parse_literal() { - return literal; + return Some(literal); } if let Some(kind) = self.parse_parentheses_expression() { - return kind; + return Some(kind); } if let Some(kind) = self.parse_unsafe_expr() { - return kind; + return Some(kind); } if let Some(kind) = self.parse_path_expr(allow_constructos) { - return kind; + return Some(kind); } if let Some(kind) = self.parse_if_expr() { - return kind; + return Some(kind); } - ExpressionKind::Error + None } fn parse_unsafe_expr(&mut self) -> Option { @@ -221,7 +250,7 @@ impl<'a> Parser<'a> { } if self.eat_colon() { - let expression = self.parse_expression(); + let expression = self.parse_expression_or_error(); fields.push((ident, expression)); } else { fields.push((ident.clone(), ident.into())); @@ -246,7 +275,7 @@ impl<'a> Parser<'a> { return None; } - let condition = self.parse_expression_no_constructors(); + let condition = self.parse_expression_no_constructors_or_error(); let start_span = self.current_token_span; let Some(consequence) = self.parse_block_expression() else { @@ -362,13 +391,13 @@ impl<'a> Parser<'a> { } let start_span = self.current_token_span; - let first_expr = self.parse_expression(); + let first_expr = self.parse_expression_or_error(); if self.current_token_span == start_span { return Some(ArrayLiteral::Standard(Vec::new())); } if self.eat_semicolon() { - let length = self.parse_expression(); + let length = self.parse_expression_or_error(); if !self.eat_right_bracket() { if is_slice { self.push_error( @@ -396,11 +425,10 @@ impl<'a> Parser<'a> { } let start_span = self.current_token_span; - let expr = self.parse_expression(); - if self.current_token_span == start_span { + let Some(expr) = self.parse_expression() else { self.eat_right_brace(); break; - } + }; if !trailing_comma { self.push_error(ParserErrorReason::MissingCommaSeparatingExpressions, start_span); @@ -427,12 +455,14 @@ impl<'a> Parser<'a> { let mut trailing_comma = false; loop { let start_span = self.current_token_span; - let expr = self.parse_expression(); - if let ExpressionKind::Error = expr.kind { - self.push_error(ParserErrorReason::ExpectedExpression, start_span); + let Some(expr) = self.parse_expression() else { + self.push_error( + ParserErrorReason::ExpectedExpressionAfterThis, + self.previous_token_span, + ); self.eat_right_paren(); break; - } + }; if !trailing_comma && !exprs.is_empty() { self.push_error(ParserErrorReason::MissingCommaSeparatingExpressions, start_span); } @@ -497,12 +527,12 @@ mod tests { fn parses_bool_literals() { let src = "true"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(true)))); let src = "false"; - let expr = Parser::for_str(src).parse_expression(); + let expr = Parser::for_str(src).parse_expression_or_error(); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(false)))); } @@ -510,7 +540,7 @@ mod tests { fn parses_integer_literal() { let src = "42"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); @@ -523,7 +553,7 @@ mod tests { fn parses_parenthesized_expression() { let src = "(42)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); @@ -539,7 +569,7 @@ mod tests { fn parses_unit() { let src = "()"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Unit))); } @@ -548,7 +578,7 @@ mod tests { fn parses_str() { let src = "\"hello\""; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Str(string)) = expr.kind else { panic!("Expected string literal"); @@ -560,7 +590,7 @@ mod tests { fn parses_raw_str() { let src = "r#\"hello\"#"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::RawStr(string, n)) = expr.kind else { panic!("Expected raw string literal"); @@ -573,7 +603,7 @@ mod tests { fn parses_fmt_str() { let src = "f\"hello\""; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::FmtStr(string)) = expr.kind else { panic!("Expected format string literal"); @@ -585,7 +615,7 @@ mod tests { fn parses_tuple_expression() { let src = "(1, 2)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Tuple(mut exprs) = expr.kind else { panic!("Expected tuple expression"); @@ -611,7 +641,7 @@ mod tests { fn parses_block_expression_with_a_single_expression() { let src = "{ 1 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Block(mut block) = expr.kind else { panic!("Expected block expression"); @@ -640,7 +670,7 @@ mod tests { } "; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Block(block) = expr.kind else { panic!("Expected block expression"); @@ -655,7 +685,7 @@ mod tests { fn parses_unsafe_expression() { let src = "unsafe { 1 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Unsafe(block, _) = expr.kind else { panic!("Expected unsafe expression"); @@ -673,7 +703,7 @@ mod tests { let mut parser = Parser::for_str(&src); parser.parse_expression(); let reason = get_single_error(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ExpectedExpression)); + assert!(matches!(reason, ParserErrorReason::ExpectedExpressionAfterThis)); } #[test] @@ -693,7 +723,7 @@ mod tests { fn parses_empty_array_expression() { let src = "[]"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -706,7 +736,7 @@ mod tests { fn parses_array_expression_with_one_element() { let src = "[1]"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -720,7 +750,7 @@ mod tests { fn parses_array_expression_with_two_elements() { let src = "[1, 3]"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -735,7 +765,7 @@ mod tests { fn parses_repeated_array_expression() { let src = "[1; 10]"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { repeated_element, @@ -752,7 +782,7 @@ mod tests { fn parses_empty_slice_expression() { let src = "&[]"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -765,7 +795,7 @@ mod tests { fn parses_variable_ident() { let src = "foo"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); @@ -777,7 +807,7 @@ mod tests { fn parses_variable_path() { let src = "foo::bar"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); @@ -789,7 +819,7 @@ mod tests { fn parses_mutable_ref() { let src = "&mut foo"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -806,7 +836,7 @@ mod tests { fn parses_minus() { let src = "-foo"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -823,7 +853,7 @@ mod tests { fn parses_not() { let src = "!foo"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -840,7 +870,7 @@ mod tests { fn parses_dereference() { let src = "*foo"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -857,7 +887,7 @@ mod tests { fn parses_quote() { let src = "quote { 1 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Quote(tokens) = expr.kind else { panic!("Expected quote expression"); @@ -869,7 +899,7 @@ mod tests { fn parses_call() { let src = "foo(1, 2)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); @@ -883,7 +913,7 @@ mod tests { fn parses_call_with_turbofish() { let src = "foo::(1, 2)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); @@ -897,7 +927,7 @@ mod tests { fn parses_macro_call() { let src = "foo!(1, 2)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); @@ -911,7 +941,7 @@ mod tests { fn parses_member_access() { let src = "foo.bar"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::MemberAccess(member_access) = expr.kind else { panic!("Expected member access expression"); @@ -924,7 +954,7 @@ mod tests { fn parses_method_call() { let src = "foo.bar(1, 2)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); @@ -940,7 +970,7 @@ mod tests { fn parses_method_call_with_turbofish() { let src = "foo.bar::(1, 2)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); @@ -956,7 +986,7 @@ mod tests { fn parses_method_macro_call() { let src = "foo.bar!(1, 2)"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); @@ -972,7 +1002,7 @@ mod tests { fn parses_empty_constructor() { let src = "Foo {}"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Constructor(constructor) = expr.kind else { panic!("Expected constructor"); @@ -985,7 +1015,7 @@ mod tests { fn parses_constructor_with_fields() { let src = "Foo { x: 1, y, z: 2 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert_eq!(expr.span.end() as usize, src.len()); assert!(parser.errors.is_empty()); let ExpressionKind::Constructor(mut constructor) = expr.kind else { @@ -1011,7 +1041,7 @@ mod tests { fn parses_parses_if_true() { let src = "if true { 1 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1029,7 +1059,7 @@ mod tests { fn parses_parses_if_var() { let src = "if foo { 1 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1041,7 +1071,7 @@ mod tests { fn parses_parses_if_else() { let src = "if true { 1 } else { 2 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1054,7 +1084,7 @@ mod tests { fn parses_parses_if_else_if() { let src = "if true { 1 } else if false { 2 } else { 3 }"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1069,7 +1099,7 @@ mod tests { fn parses_cast() { let src = "1 as u8"; let mut parser = Parser::for_str(src); - let expr = parser.parse_expression(); + let expr = parser.parse_expression_or_error(); assert!(parser.errors.is_empty()); let ExpressionKind::Cast(cast_expr) = expr.kind else { panic!("Expected cast"); diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index 15b0a126034..29020b88d64 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -42,7 +42,7 @@ impl<'a> Parser<'a> { let typ = self.parse_optional_type_annotation(); let expression = if self.eat_assign() { - self.parse_expression() + self.parse_expression_or_error() } else { self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.span()); Expression { kind: ExpressionKind::Error, span: Span::default() } diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index c684d95e3bb..a60cb85b51a 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -227,7 +227,7 @@ impl<'a> Parser<'a> { }; let expr = if self.eat_assign() { - self.parse_expression() + self.parse_expression_or_error() } else { // TODO: error Expression { kind: ExpressionKind::Error, span: Span::default() } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 7c90ccd7580..7b1f4dd6dd4 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -50,7 +50,7 @@ impl<'a> Parser<'a> { return self.parse_comptime_statement(attributes).unwrap(); } - StatementKind::Expression(self.parse_expression()) + StatementKind::Expression(self.parse_expression_or_error()) } fn parse_comptime_statement( @@ -93,7 +93,7 @@ impl<'a> Parser<'a> { let pattern = self.parse_pattern(); let r#type = self.parse_optional_type_annotation(); let expression = if self.eat_assign() { - self.parse_expression() + self.parse_expression_or_error() } else { // TODO: error Expression { kind: ExpressionKind::Error, span: self.current_token_span } @@ -121,7 +121,7 @@ impl<'a> Parser<'a> { ConstrainKind::Constrain => { self.push_error(ParserErrorReason::ConstrainDeprecated, self.previous_token_span); - let expression = self.parse_expression(); + let expression = self.parse_expression_or_error(); ConstrainStatement { kind, arguments: vec![expression], diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index ce7ae4bb9b5..09fb3576768 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -124,7 +124,8 @@ impl<'a> Parser<'a> { UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } }; - let default_value = if self.eat_assign() { Some(self.parse_expression()) } else { None }; + let default_value = + if self.eat_assign() { Some(self.parse_expression_or_error()) } else { None }; self.eat_semicolons(); diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 12559fc39ad..c774a7c13ac 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,4 +1,9 @@ -use crate::{ast::UnresolvedTypeExpression, parser::ParserError}; +use noirc_errors::Span; + +use crate::{ + ast::{Expression, ExpressionKind, UnresolvedTypeExpression}, + parser::ParserError, +}; use super::Parser; @@ -6,7 +11,10 @@ impl<'a> Parser<'a> { pub(crate) fn parse_type_expression( &mut self, ) -> Result { - let expr = self.parse_expression(); + let expr = self.parse_expression().unwrap_or(Expression { + kind: ExpressionKind::Error, + span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), + }); let span = expr.span; UnresolvedTypeExpression::from_expr(expr, span) } From ff703e7edc1d3829db377b15a553e58fed232c7d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 09:54:29 -0300 Subject: [PATCH 104/229] Better way to parse statements --- .../src/hir/comptime/interpreter/builtin.rs | 3 +- compiler/noirc_frontend/src/parser/errors.rs | 2 + .../src/parser/parser/expression.rs | 9 +-- .../src/parser/parser/statement.rs | 63 ++++++++++++------- 4 files changed, 49 insertions(+), 28 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 6937efa97b6..2997defc1bd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -684,7 +684,8 @@ fn quoted_as_expr( return option(return_type, Some(Value::expression(expr.kind))); } - let result = parse(interner, argument.clone(), Parser::parse_statement, "an expression"); + 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))); } diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index e959b460f73..94014e1da2f 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -73,6 +73,8 @@ pub enum ParserErrorReason { ExpectedTypeAfterThis, #[error("Expected expression after this")] ExpectedExpressionAfterThis, + #[error("Expected statement after this")] + ExpectedStatementAfterThis, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index f379b332eb0..a9cb3e89b8d 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -390,9 +390,8 @@ impl<'a> Parser<'a> { return Some(ArrayLiteral::Standard(Vec::new())); } - let start_span = self.current_token_span; let first_expr = self.parse_expression_or_error(); - if self.current_token_span == start_span { + if first_expr.kind == ExpressionKind::Error { return Some(ArrayLiteral::Standard(Vec::new())); } @@ -495,13 +494,11 @@ impl<'a> Parser<'a> { break; } - let start_span = self.current_token_span; - let statement = self.parse_statement(); - if self.current_token_span == start_span { + let Some(statement) = self.parse_statement() else { // TODO: error? self.eat_right_brace(); break; - } + }; statements.push(statement); diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 7b1f4dd6dd4..e0f9d00a5fb 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -12,45 +12,66 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - pub(crate) fn parse_statement(&mut self) -> Statement { + pub(crate) fn parse_statement_or_error(&mut self) -> Statement { + if let Some(statement) = self.parse_statement() { + statement + } else { + self.push_error( + ParserErrorReason::ExpectedStatementAfterThis, + self.previous_token_span, + ); + Statement { + kind: StatementKind::Error, + span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), + } + } + } + + pub(crate) fn parse_statement(&mut self) -> Option { let attributes = self.parse_attributes(); let start_span = self.current_token_span; - let kind = self.parse_statement_kind(attributes); + let kind = self.parse_statement_kind(attributes)?; let span = self.span_since(start_span); - Statement { kind, span } + Some(Statement { kind, span }) } - fn parse_statement_kind(&mut self, attributes: Vec<(Attribute, Span)>) -> StatementKind { + fn parse_statement_kind( + &mut self, + attributes: Vec<(Attribute, Span)>, + ) -> Option { if let Some(token) = self.eat_kind(TokenKind::InternedStatement) { match token.into_token() { - Token::InternedStatement(statement) => return StatementKind::Interned(statement), + Token::InternedStatement(statement) => { + return Some(StatementKind::Interned(statement)) + } _ => unreachable!(), } } if self.eat_keyword(Keyword::Break) { - return StatementKind::Break; + return Some(StatementKind::Break); } if self.eat_keyword(Keyword::Continue) { - return StatementKind::Continue; + return Some(StatementKind::Continue); } if self.token.token() == &Token::Keyword(Keyword::Let) { - let let_statement = self.parse_let_statement(attributes).unwrap(); - return StatementKind::Let(let_statement); + let let_statement = self.parse_let_statement(attributes)?; + return Some(StatementKind::Let(let_statement)); } if let Some(constrain) = self.parse_constrain_statement() { - return StatementKind::Constrain(constrain); + return Some(StatementKind::Constrain(constrain)); } if self.token.token() == &Token::Keyword(Keyword::Comptime) { - return self.parse_comptime_statement(attributes).unwrap(); + return self.parse_comptime_statement(attributes); } - StatementKind::Expression(self.parse_expression_or_error()) + let expression = self.parse_expression()?; + Some(StatementKind::Expression(expression)) } fn parse_comptime_statement( @@ -158,7 +179,7 @@ mod tests { fn parses_break() { let src = "break"; let mut parser = Parser::for_str(src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); assert!(matches!(statement.kind, StatementKind::Break)); } @@ -167,7 +188,7 @@ mod tests { fn parses_continue() { let src = "continue"; let mut parser = Parser::for_str(src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); assert!(matches!(statement.kind, StatementKind::Continue)); } @@ -176,7 +197,7 @@ mod tests { fn parses_let_statement_no_type() { let src = "let x = 1;"; let mut parser = Parser::for_str(src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); @@ -191,7 +212,7 @@ mod tests { fn parses_let_statement_with_type() { let src = "let x: Field = 1;"; let mut parser = Parser::for_str(src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); @@ -206,7 +227,7 @@ mod tests { fn parses_assert() { let src = "assert(true, \"good\")"; let mut parser = Parser::for_str(src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); @@ -219,7 +240,7 @@ mod tests { fn parses_assert_eq() { let src = "assert_eq(1, 2, \"bad\")"; let mut parser = Parser::for_str(src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); @@ -236,7 +257,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -251,7 +272,7 @@ mod tests { fn parses_comptime_block() { let src = "comptime { 1 }"; let mut parser = Parser::for_str(&src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); @@ -269,7 +290,7 @@ mod tests { fn parses_comptime_let() { let src = "comptime let x = 1;"; let mut parser = Parser::for_str(&src); - let statement = parser.parse_statement(); + let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); From 9852c96dd273b3317537d0cf4d26a0cb512b64e4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 10:03:39 -0300 Subject: [PATCH 105/229] Parse index expression --- .../src/parser/parser/expression.rs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a9cb3e89b8d..69d4817e9b9 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -3,8 +3,9 @@ use noirc_errors::Span; use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, - Expression, ExpressionKind, Ident, IfExpression, Literal, MemberAccessExpression, - MethodCallExpression, Path, PrefixExpression, UnaryOp, UnresolvedType, + Expression, ExpressionKind, Ident, IfExpression, IndexExpression, Literal, + MemberAccessExpression, MethodCallExpression, Path, PrefixExpression, UnaryOp, + UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token}, @@ -167,6 +168,18 @@ impl<'a> Parser<'a> { continue; } + if self.eat_left_bracket() { + let index = self.parse_expression_or_error(); + if !self.eat_right_bracket() { + // TODO: error + } + let kind = + ExpressionKind::Index(Box::new(IndexExpression { collection: atom, index })); + let span = self.span_since(start_span); + atom = Expression { kind, span }; + continue; + } + break; } @@ -1117,4 +1130,17 @@ mod tests { let reason = get_single_error(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedTypeAfterThis)); } + + #[test] + fn parses_index() { + let src = "1[2]"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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"); + } } From c2b1f921373b67cf45c083dd76483e54d15928f5 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 11:40:49 -0300 Subject: [PATCH 106/229] Parse type expressions in a better way --- compiler/noirc_frontend/src/ast/expression.rs | 2 +- compiler/noirc_frontend/src/parser/errors.rs | 8 +- compiler/noirc_frontend/src/parser/parser.rs | 1 + .../src/parser/parser/expression.rs | 78 ++++-- .../noirc_frontend/src/parser/parser/infix.rs | 237 ++++++++++++++++++ .../src/parser/parser/type_expression.rs | 183 +++++++++++++- 6 files changed, 473 insertions(+), 36 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/infix.rs diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 91eab68cc7d..d6e705cbbea 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -299,7 +299,7 @@ impl Expression { pub type BinaryOp = Spanned; -#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)] +#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone, strum_macros::EnumIter)] pub enum BinaryOpKind { Add, Subtract, diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 94014e1da2f..c8ce51e2118 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -69,12 +69,14 @@ pub enum ParserErrorReason { ExpectedStringTypeLength, #[error("Expected `{{` or `if` after `else`")] ExpectedLeftBraceOfIfAfterElse, - #[error("Expected type after this")] + #[error("Expected a type after this")] ExpectedTypeAfterThis, - #[error("Expected expression after this")] + #[error("Expected an expression after this")] ExpectedExpressionAfterThis, - #[error("Expected statement after this")] + #[error("Expected a statement after this")] ExpectedStatementAfterThis, + #[error("Expected a type expression after this")] + ExpectedTypeExpressionAfterThis, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index a113d0d51e9..1ac501da224 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -17,6 +17,7 @@ mod function; mod generics; mod global; mod impls; +mod infix; mod item; mod item_visibility; mod modifiers; diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 69d4817e9b9..a5bde2c1234 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -13,8 +13,6 @@ use crate::{ use super::Parser; -// term -> atom_or_right_unary -> atom - impl<'a> Parser<'a> { pub(crate) fn parse_expression_or_error(&mut self) -> Expression { self.parse_expression_or_error_impl(true) // allow constructors @@ -30,14 +28,14 @@ impl<'a> Parser<'a> { self.parse_expression_or_error_impl(false) // allow constructors } - pub(crate) fn parse_expression_or_error_impl(&mut self, allow_constructos: bool) -> Expression { - if let Some(expr) = self.parse_expression_impl(allow_constructos) { + 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_error( - ParserErrorReason::ExpectedExpressionAfterThis, - self.previous_token_span, - ); + self.push_expected_expression_after_this_error(); Expression { kind: ExpressionKind::Error, span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), @@ -45,14 +43,14 @@ impl<'a> Parser<'a> { } } - fn parse_expression_impl(&mut self, allow_constructos: bool) -> Option { - self.parse_term(allow_constructos) + fn parse_expression_impl(&mut self, allow_constructors: bool) -> Option { + self.parse_equal_or_not_equal(allow_constructors) } - fn parse_term(&mut self, allow_constructos: bool) -> Option { + 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_constructos) else { + let Some(rhs) = self.parse_term(allow_constructors) else { self.push_error( ParserErrorReason::ExpectedExpressionAfterThis, self.previous_token_span, @@ -64,7 +62,7 @@ impl<'a> Parser<'a> { return Some(Expression { kind, span }); } - self.parse_atom_or_unary_right(allow_constructos) + self.parse_atom_or_unary_right(allow_constructors) } fn parse_unary_op(&mut self) -> Option { @@ -85,9 +83,9 @@ impl<'a> Parser<'a> { } } - fn parse_atom_or_unary_right(&mut self, allow_constructos: bool) -> Option { + 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_constructos)?; + let mut atom = self.parse_atom(allow_constructors)?; loop { let is_macro_call = @@ -186,13 +184,13 @@ impl<'a> Parser<'a> { Some(atom) } - fn parse_atom(&mut self, allow_constructos: bool) -> Option { + fn parse_atom(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; - let kind = self.parse_atom_kind(allow_constructos)?; + let kind = self.parse_atom_kind(allow_constructors)?; Some(Expression { kind, span: self.span_since(start_span) }) } - fn parse_atom_kind(&mut self, allow_constructos: bool) -> Option { + fn parse_atom_kind(&mut self, allow_constructors: bool) -> Option { if let Some(literal) = self.parse_literal() { return Some(literal); } @@ -205,7 +203,7 @@ impl<'a> Parser<'a> { return Some(kind); } - if let Some(kind) = self.parse_path_expr(allow_constructos) { + if let Some(kind) = self.parse_path_expr(allow_constructors) { return Some(kind); } @@ -229,13 +227,13 @@ impl<'a> Parser<'a> { } } - fn parse_path_expr(&mut self, allow_constructos: bool) -> Option { + fn parse_path_expr(&mut self, allow_constructors: bool) -> Option { let path = self.parse_path(); if path.is_empty() { return None; } - if allow_constructos && self.eat_left_brace() { + if allow_constructors && self.eat_left_brace() { return Some(self.parse_constructor(path)); } @@ -521,12 +519,18 @@ impl<'a> Parser<'a> { Some(BlockExpression { statements }) } + + pub(super) fn push_expected_expression_after_this_error(&mut self) { + self.push_error(ParserErrorReason::ExpectedExpressionAfterThis, self.previous_token_span); + } } #[cfg(test)] mod tests { + use strum::IntoEnumIterator; + use crate::{ - ast::{ArrayLiteral, ExpressionKind, Literal, StatementKind, UnaryOp}, + ast::{ArrayLiteral, BinaryOpKind, ExpressionKind, Literal, StatementKind, UnaryOp}, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, @@ -1143,4 +1147,34 @@ mod tests { 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!(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() { + let src = "1 + 2 * 3 + 4"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Infix(infix_expr) = expr.kind else { + panic!("Expected infix"); + }; + assert_eq!(infix_expr.lhs.to_string(), "(1 + (2 * 3))"); + assert_eq!(infix_expr.operator.contents, BinaryOpKind::Add); + assert_eq!(infix_expr.rhs.to_string(), "4"); + } } 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..7b71e514b7b --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -0,0 +1,237 @@ +use noirc_errors::{Span, Spanned}; + +use crate::{ + ast::{BinaryOpKind, Expression, ExpressionKind, InfixExpression}, + token::Token, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(super) fn parse_equal_or_not_equal( + &mut self, + allow_constructors: bool, + ) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_or(allow_constructors)?; + + loop { + let operator = if self.eat(Token::Equal) { + BinaryOpKind::Equal + } else if self.eat(Token::NotEqual) { + BinaryOpKind::NotEqual + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_or(allow_constructors) else { + self.push_expected_expression_after_this_error(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + pub(super) fn parse_or(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_and(allow_constructors)?; + + loop { + let operator = if self.eat(Token::Pipe) { + BinaryOpKind::Or + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_and(allow_constructors) else { + self.push_expected_expression_after_this_error(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + pub(super) fn parse_and(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_xor(allow_constructors)?; + + loop { + let operator = if self.eat(Token::Ampersand) { + BinaryOpKind::And + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_xor(allow_constructors) else { + self.push_expected_expression_after_this_error(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + pub(super) fn parse_xor(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_less_or_greater(allow_constructors)?; + + loop { + let operator = if self.eat(Token::Caret) { + BinaryOpKind::Xor + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_less_or_greater(allow_constructors) else { + self.push_expected_expression_after_this_error(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + pub(super) fn parse_less_or_greater(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_shift(allow_constructors)?; + + loop { + let operator = if self.eat(Token::Less) { + BinaryOpKind::Less + } else if self.eat(Token::LessEqual) { + BinaryOpKind::LessEqual + } else if self.eat(Token::Greater) { + BinaryOpKind::Greater + } else if self.eat(Token::GreaterEqual) { + BinaryOpKind::GreaterEqual + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_shift(allow_constructors) else { + self.push_expected_expression_after_this_error(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + pub(super) fn parse_shift(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_add_or_subtract(allow_constructors)?; + + loop { + let operator = if self.eat(Token::ShiftLeft) { + BinaryOpKind::ShiftLeft + } else + // 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. + if self.token.token() == &Token::Greater + && self.next_token.token() == &Token::Greater + { + self.next_token(); + self.next_token(); + BinaryOpKind::ShiftRight + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_add_or_subtract(allow_constructors) else { + self.push_expected_expression_after_this_error(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + pub(super) fn parse_add_or_subtract(&mut self, allow_constructors: bool) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_multiply_or_divide_or_modulo(allow_constructors)?; + + loop { + let operator = if self.eat(Token::Plus) { + BinaryOpKind::Add + } else if self.eat(Token::Minus) { + BinaryOpKind::Subtract + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_multiply_or_divide_or_modulo(allow_constructors) else { + self.push_expected_expression_after_this_error(); + break; + }; + + lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + } + + Some(lhs) + } + + pub(super) fn parse_multiply_or_divide_or_modulo( + &mut self, + allow_constructors: bool, + ) -> Option { + let start_span = self.current_token_span; + let mut lhs = self.parse_term(allow_constructors)?; + + loop { + let operator = if self.eat(Token::Star) { + BinaryOpKind::Multiply + } else if self.eat(Token::Slash) { + BinaryOpKind::Divide + } else if self.eat(Token::Percent) { + BinaryOpKind::Modulo + } else { + break; + }; + let operator = Spanned::from(self.previous_token_span, operator); + + let Some(rhs) = self.parse_term(allow_constructors) else { + self.push_expected_expression_after_this_error(); + 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/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index c774a7c13ac..a961ebc5453 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,21 +1,184 @@ -use noirc_errors::Span; - use crate::{ - ast::{Expression, ExpressionKind, UnresolvedTypeExpression}, - parser::ParserError, + ast::{Expression, ExpressionKind, Literal, UnresolvedTypeExpression}, + parser::{ParserError, ParserErrorReason}, + token::Token, + BinaryTypeOperator, }; +use acvm::acir::AcirField; + use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_type_expression( &mut self, ) -> Result { - let expr = self.parse_expression().unwrap_or(Expression { - kind: ExpressionKind::Error, - span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), - }); - let span = expr.span; - UnresolvedTypeExpression::from_expr(expr, span) + self.parse_add_or_subtract_type_expression() + } + + fn parse_add_or_subtract_type_expression( + &mut self, + ) -> Result { + let start_span = self.current_token_span; + let mut lhs = self.parse_multiply_or_divide_or_modulo_type_expression()?; + + 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() { + Ok(rhs) => { + let span = self.span_since(start_span); + lhs = UnresolvedTypeExpression::BinaryOperation( + Box::new(lhs), + operator, + Box::new(rhs), + span, + ); + } + Err(err) => { + self.errors.push(err); + break; + } + } + } + + Ok(lhs) + } + + fn parse_multiply_or_divide_or_modulo_type_expression( + &mut self, + ) -> Result { + let start_span = self.current_token_span; + let mut lhs = self.parse_term_type_expression()?; + + 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() { + Ok(rhs) => { + let span = self.span_since(start_span); + lhs = UnresolvedTypeExpression::BinaryOperation( + Box::new(lhs), + operator, + Box::new(rhs), + span, + ); + } + Err(err) => { + self.errors.push(err); + break; + } + } + } + + Ok(lhs) + } + + fn parse_term_type_expression(&mut self) -> Result { + let result = self.parses_variable_type_expression(); + if let Ok(type_expr) = result { + return Ok(type_expr); + } + + let result = self.parse_constant_type_expression(); + if let Ok(type_expr) = result { + return Ok(type_expr); + } + + result + } + + fn parse_constant_type_expression(&mut self) -> Result { + let Some(int) = self.eat_int() else { + return self.expected_type_expression_after_this(); + }; + + let Some(int) = int.try_to_u32() else { + let err_expr = Expression { + kind: ExpressionKind::Literal(Literal::Integer(int, false)), + span: self.previous_token_span, + }; + return Err(ParserError::with_reason( + ParserErrorReason::InvalidTypeExpression(err_expr), + self.previous_token_span, + )); + }; + + Ok(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) + } + + fn parses_variable_type_expression(&mut self) -> Result { + let path = self.parse_path(); + if path.is_empty() { + return self.expected_type_expression_after_this(); + } + + Ok(UnresolvedTypeExpression::Variable(path)) + } + + fn expected_type_expression_after_this( + &mut self, + ) -> Result { + Err(ParserError::with_reason( + ParserErrorReason::ExpectedTypeExpressionAfterThis, + self.previous_token_span, + )) + } +} + +#[cfg(test)] +mod tests { + use crate::{ast::UnresolvedTypeExpression, parser::Parser, BinaryTypeOperator}; + + #[test] + fn parses_constant_type_expression() { + let src = "42"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeExpression::Constant(n, _) = expr else { + panic!("Expected constant"); + }; + assert_eq!(n, 42); + } + + #[test] + fn parses_variable_type_expression() { + let src = "foo::bar"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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"); } } From 4aac1c92c583883865db76a423d5228b4c303243 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 14:22:00 -0300 Subject: [PATCH 107/229] Refactor --- .../src/parser/parser/type_expression.rs | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index a961ebc5453..79b2e39b990 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -13,12 +13,13 @@ impl<'a> Parser<'a> { pub(crate) fn parse_type_expression( &mut self, ) -> Result { - self.parse_add_or_subtract_type_expression() + match self.parse_add_or_subtract_type_expression() { + Some(type_expr) => Ok(type_expr), + None => self.expected_type_expression_after_this(), + } } - fn parse_add_or_subtract_type_expression( - &mut self, - ) -> Result { + fn parse_add_or_subtract_type_expression(&mut self) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_multiply_or_divide_or_modulo_type_expression()?; @@ -32,7 +33,7 @@ impl<'a> Parser<'a> { }; match self.parse_multiply_or_divide_or_modulo_type_expression() { - Ok(rhs) => { + Some(rhs) => { let span = self.span_since(start_span); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), @@ -41,19 +42,18 @@ impl<'a> Parser<'a> { span, ); } - Err(err) => { - self.errors.push(err); - break; + None => { + self.push_expected_expression_after_this_error(); } } } - Ok(lhs) + Some(lhs) } fn parse_multiply_or_divide_or_modulo_type_expression( &mut self, - ) -> Result { + ) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_term_type_expression()?; @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { }; match self.parse_term_type_expression() { - Ok(rhs) => { + Some(rhs) => { let span = self.span_since(start_span); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), @@ -78,56 +78,57 @@ impl<'a> Parser<'a> { span, ); } - Err(err) => { - self.errors.push(err); + None => { + self.push_expected_expression_after_this_error(); break; } } } - Ok(lhs) + Some(lhs) } - fn parse_term_type_expression(&mut self) -> Result { - let result = self.parses_variable_type_expression(); - if let Ok(type_expr) = result { - return Ok(type_expr); + fn parse_term_type_expression(&mut self) -> Option { + if let Some(type_expr) = self.parse_constant_type_expression() { + return Some(type_expr); } - let result = self.parse_constant_type_expression(); - if let Ok(type_expr) = result { - return Ok(type_expr); + if let Some(type_expr) = self.parses_variable_type_expression() { + return Some(type_expr); } - result + None } - fn parse_constant_type_expression(&mut self) -> Result { + fn parse_constant_type_expression(&mut self) -> Option { let Some(int) = self.eat_int() else { - return self.expected_type_expression_after_this(); + return None; }; - let Some(int) = int.try_to_u32() else { + 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, }; - return Err(ParserError::with_reason( + self.push_error( ParserErrorReason::InvalidTypeExpression(err_expr), self.previous_token_span, - )); + ); + 0 }; - Ok(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) + Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) } - fn parses_variable_type_expression(&mut self) -> Result { + fn parses_variable_type_expression(&mut self) -> Option { let path = self.parse_path(); if path.is_empty() { - return self.expected_type_expression_after_this(); + None + } else { + Some(UnresolvedTypeExpression::Variable(path)) } - - Ok(UnresolvedTypeExpression::Variable(path)) } fn expected_type_expression_after_this( From ad58443e770ef9bc50dc6c3f6d65ceca9db4b360 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 14:34:09 -0300 Subject: [PATCH 108/229] Interned and resolved exprs --- .../src/parser/parser/expression.rs | 70 ++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a5bde2c1234..64697a222d7 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -8,7 +8,7 @@ use crate::{ UnresolvedType, }, parser::ParserErrorReason, - token::{Keyword, Token}, + token::{Keyword, Token, TokenKind}, }; use super::Parser; @@ -211,6 +211,58 @@ impl<'a> Parser<'a> { return Some(kind); } + // TODO: parse these too + // lambdas::lambda(expr_parser.clone()), + // comptime_expr(statement.clone()), + // unquote(expr_parser.clone()), + // as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), + // type_path(parse_type()), + + 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 + } + + 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 + } + + 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 + } + + 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 } @@ -368,22 +420,6 @@ impl<'a> Parser<'a> { return Some(ExpressionKind::Block(kind)); } - // TODO: parse these too - // if_expr(expr_no_constructors, statement.clone()), - // if allow_constructors { - // constructor(expr_parser.clone()).boxed() - // } else { - // nothing().boxed() - // }, - // lambdas::lambda(expr_parser.clone()), - // comptime_expr(statement.clone()), - // unquote(expr_parser.clone()), - // as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), - // type_path(parse_type()), - // macro_quote_marker(), - // interned_expr(), - // interned_statement_expr(), - None } From 4ba842912efcd1221ed0ac1b10f9bced52172e6e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 15:30:43 -0300 Subject: [PATCH 109/229] Parse lambdas --- compiler/noirc_frontend/src/parser/parser.rs | 5 ++ .../src/parser/parser/expression.rs | 58 +++++++++++++++- .../src/parser/parser/lambda.rs | 69 +++++++++++++++++++ 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/lambda.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 1ac501da224..18b2ba33b05 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -20,6 +20,7 @@ mod impls; mod infix; mod item; mod item_visibility; +mod lambda; mod modifiers; mod module; mod path; @@ -373,6 +374,10 @@ impl<'a> Parser<'a> { self.eat(Token::Dot) } + fn eat_pipe(&mut self) -> bool { + self.eat(Token::Pipe) + } + fn eat(&mut self, token: Token) -> bool { if self.token.token() == &token { self.next_token(); diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 64697a222d7..e1a2c13db71 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -211,8 +211,11 @@ impl<'a> Parser<'a> { return Some(kind); } + if let Some(kind) = self.parse_lambda() { + return Some(kind); + } + // TODO: parse these too - // lambdas::lambda(expr_parser.clone()), // comptime_expr(statement.clone()), // unquote(expr_parser.clone()), // as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), @@ -566,7 +569,10 @@ mod tests { use strum::IntoEnumIterator; use crate::{ - ast::{ArrayLiteral, BinaryOpKind, ExpressionKind, Literal, StatementKind, UnaryOp}, + ast::{ + ArrayLiteral, BinaryOpKind, ExpressionKind, Literal, StatementKind, UnaryOp, + UnresolvedTypeData, + }, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, @@ -1213,4 +1219,52 @@ mod tests { assert_eq!(infix_expr.operator.contents, BinaryOpKind::Add); assert_eq!(infix_expr.rhs.to_string(), "4"); } + + #[test] + fn parses_empty_lambda() { + let src = "|| 1"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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)); + } } 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..9e483d22de8 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -0,0 +1,69 @@ +use noirc_errors::Span; + +use crate::{ + ast::{ExpressionKind, Lambda, Pattern, UnresolvedType, UnresolvedTypeData}, + parser::ParserErrorReason, + token::Token, +}; + +use super::Parser; + +impl<'a> Parser<'a> { + 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 { + UnresolvedTypeData::Unspecified.with_span(Span::from( + self.previous_token_span.end()..self.previous_token_span.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)> { + let mut parameters = Vec::new(); + let mut trailing_comma = false; + + loop { + if self.eat_pipe() { + break; + } + + let start_span = self.current_token_span; + let pattern = self.parse_pattern(); + if self.current_token_span == start_span { + // An error was already produced by parse_pattern(). + // Let's try with the next token. + self.next_token(); + if self.is_eof() { + break; + } + continue; + } + + if !trailing_comma && !parameters.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); + } + + let typ = if self.eat_colon() { + self.parse_type_or_error() + } else { + UnresolvedTypeData::Unspecified.with_span(Span::from( + self.previous_token_span.end()..self.previous_token_span.end(), + )) + }; + parameters.push((pattern, typ)); + + trailing_comma = self.eat_commas(); + } + + parameters + } +} From 0a2e7d57d184cee2a71345d88d9476de2e5e3e4c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 15:48:19 -0300 Subject: [PATCH 110/229] Parse for --- .../src/parser/parser/statement.rs | 88 ++++++++++++++++++- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index e0f9d00a5fb..f70c81495a9 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -2,8 +2,8 @@ use noirc_errors::Span; use crate::{ ast::{ - ConstrainKind, ConstrainStatement, Expression, ExpressionKind, LetStatement, Statement, - StatementKind, + ConstrainKind, ConstrainStatement, Expression, ExpressionKind, ForLoopStatement, ForRange, + Ident, LetStatement, Statement, StatementKind, }, parser::ParserErrorReason, token::{Attribute, Keyword, Token, TokenKind}, @@ -70,10 +70,66 @@ impl<'a> Parser<'a> { return self.parse_comptime_statement(attributes); } + if let Some(for_loop) = self.parse_for() { + return Some(StatementKind::For(for_loop)); + } + let expression = self.parse_expression()?; Some(StatementKind::Expression(expression)) } + 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 { + // TODO: error (expected for identifier) + let identifier = Ident::default(); + return Some(self.empty_for_loop(identifier, start_span)); + }; + + if !self.eat_keyword(Keyword::In) { + // TODO: error (expected `in` after for identifier) + return Some(self.empty_for_loop(identifier, start_span)); + } + + let expr = self.parse_expression_no_constructors_or_error(); + + let range = if self.eat(Token::DoubleDot) { + ForRange::Range(expr, self.parse_expression_no_constructors_or_error()) + } else { + ForRange::Array(expr) + }; + + let block_start_span = self.current_token_span; + let block = if let Some(block) = self.parse_block_expression() { + Expression { + kind: ExpressionKind::Block(block), + span: self.span_since(block_start_span), + } + } else { + // TODO: error (expected for body) + Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + }; + + Some(ForLoopStatement { identifier, range, block, span: self.span_since(start_span) }) + } + + 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), + } + } + fn parse_comptime_statement( &mut self, attributes: Vec<(Attribute, Span)>, @@ -168,7 +224,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ConstrainKind, ExpressionKind, StatementKind, UnresolvedTypeData}, + ast::{ConstrainKind, ExpressionKind, ForRange, StatementKind, UnresolvedTypeData}, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, @@ -299,4 +355,30 @@ mod tests { panic!("Expected let statement"); }; } + + #[test] + fn parses_for_array() { + let src = "for i in x { }"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + 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_for_range() { + let src = "for i in 0..10 { }"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + 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::Range(..))); + } } From cf5d20ff1814b0fce01a062726e615715026093f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 15:50:12 -0300 Subject: [PATCH 111/229] Parse comptime for --- .../src/parser/parser/statement.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index f70c81495a9..19fa854c07e 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -158,6 +158,15 @@ impl<'a> Parser<'a> { }))); } + if let Some(for_loop) = self.parse_for() { + return Some(StatementKind::Comptime(Box::new(Statement { + kind: StatementKind::For(for_loop), + span: self.span_since(start_span), + }))); + } + + // TODO: error (found comptime but not a valid statement) + None } @@ -381,4 +390,20 @@ mod tests { assert_eq!(for_loop.identifier.to_string(), "i"); assert!(matches!(for_loop.range, ForRange::Range(..))); } + + #[test] + fn parses_comptime_for() { + let src = "comptime for i in x { }"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + 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(..))); + } } From f8db9faef9514e50a1dc1f9ecae3ae0c461399bd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 15:56:59 -0300 Subject: [PATCH 112/229] Have two look-ahead tokens --- compiler/noirc_frontend/src/parser/parser.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 18b2ba33b05..830c6257e6a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -79,11 +79,13 @@ pub struct Parser<'a> { errors: Vec, tokens: TokenStream<'a>, - // We always have one look-ahead token to see if we get `&mut` or just `&` - // (`&` and `mut` are two separate tokens) + // We always have two look-ahead tokens for these cases: + // - check if we get `&` or `&mut` + // - check if we get `>` or `>>` + // - check if we get `>>` or `>>=` token: SpannedToken, next_token: SpannedToken, - + next_next_token: SpannedToken, current_token_span: Span, previous_token_span: Span, } @@ -108,10 +110,11 @@ impl<'a> Parser<'a> { tokens, token: SpannedToken::default(), next_token: SpannedToken::default(), + next_next_token: SpannedToken::default(), current_token_span: Default::default(), previous_token_span: Default::default(), }; - parser.read_two_first_tokens(); + parser.read_three_first_tokens(); parser } @@ -130,15 +133,18 @@ impl<'a> Parser<'a> { self.previous_token_span = self.current_token_span; let token = self.read_token_internal(); let next_token = std::mem::take(&mut self.next_token); + let next_next_token = std::mem::take(&mut self.next_next_token); self.token = next_token; - self.next_token = token; + self.next_token = next_next_token; + self.next_next_token = token; self.current_token_span = self.token.to_span(); } - fn read_two_first_tokens(&mut self) { + fn read_three_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(); + self.next_next_token = self.read_token_internal(); } fn read_token_internal(&mut self) -> SpannedToken { From 7cb910d3cb2d381e6472f067aaa5d8f8d3d5fb67 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 16:20:10 -0300 Subject: [PATCH 113/229] Prepare to parse assignments --- .../noirc_frontend/src/parser/parser/infix.rs | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 7b71e514b7b..f811061b8dc 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -40,6 +40,11 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let mut lhs = self.parse_and(allow_constructors)?; + // Don't parse `x |= ...`, etc. + if self.next_token.token() == &Token::Assign { + return Some(lhs); + } + loop { let operator = if self.eat(Token::Pipe) { BinaryOpKind::Or @@ -63,6 +68,11 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let mut lhs = self.parse_xor(allow_constructors)?; + // Don't parse `x &= ...`, etc. + if self.next_token.token() == &Token::Assign { + return Some(lhs); + } + loop { let operator = if self.eat(Token::Ampersand) { BinaryOpKind::And @@ -86,6 +96,11 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let mut lhs = self.parse_less_or_greater(allow_constructors)?; + // Don't parse `x ^= ...`, etc. + if self.next_token.token() == &Token::Assign { + return Some(lhs); + } + loop { let operator = if self.eat(Token::Caret) { BinaryOpKind::Xor @@ -139,21 +154,23 @@ impl<'a> Parser<'a> { let mut lhs = self.parse_add_or_subtract(allow_constructors)?; loop { - let operator = if self.eat(Token::ShiftLeft) { - BinaryOpKind::ShiftLeft - } else - // 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. - if self.token.token() == &Token::Greater - && self.next_token.token() == &Token::Greater - { - self.next_token(); - self.next_token(); - BinaryOpKind::ShiftRight - } else { - break; - }; + let operator = + if self.next_token.token() != &Token::Assign && self.eat(Token::ShiftLeft) { + BinaryOpKind::ShiftLeft + } else + // 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. + if self.token.token() == &Token::Greater + && self.next_token.token() == &Token::Greater + && self.next_next_token.token() != &Token::Assign + { + self.next_token(); + self.next_token(); + BinaryOpKind::ShiftRight + } else { + break; + }; let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_add_or_subtract(allow_constructors) else { @@ -171,6 +188,11 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let mut lhs = self.parse_multiply_or_divide_or_modulo(allow_constructors)?; + // Don't parse `x += ...`, etc. + if self.next_token.token() == &Token::Assign { + return Some(lhs); + } + loop { let operator = if self.eat(Token::Plus) { BinaryOpKind::Add @@ -199,6 +221,11 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let mut lhs = self.parse_term(allow_constructors)?; + // Don't parse `x *= ...`, etc. + if self.next_token.token() == &Token::Assign { + return Some(lhs); + } + loop { let operator = if self.eat(Token::Star) { BinaryOpKind::Multiply From e40c204a4a918f53896af9f80312527d545bcffe Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 16:20:32 -0300 Subject: [PATCH 114/229] Parse assignments --- compiler/noirc_frontend/src/ast/statement.rs | 28 ++++++++--------- compiler/noirc_frontend/src/node_interner.rs | 1 + .../src/parser/parser/statement.rs | 30 +++++++++++++++++-- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 23b3070f960..05fde1da5aa 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -719,37 +719,35 @@ 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::Interned(id) => Some(LValue::Interned(id, span)), + _ => None, } } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 75178df319d..37bb32818f5 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -2131,6 +2131,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/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 19fa854c07e..80985fce719 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -2,8 +2,8 @@ use noirc_errors::Span; use crate::{ ast::{ - ConstrainKind, ConstrainStatement, Expression, ExpressionKind, ForLoopStatement, ForRange, - Ident, LetStatement, Statement, StatementKind, + AssignStatement, ConstrainKind, ConstrainStatement, Expression, ExpressionKind, + ForLoopStatement, ForRange, Ident, LValue, LetStatement, Statement, StatementKind, }, parser::ParserErrorReason, token::{Attribute, Keyword, Token, TokenKind}, @@ -75,6 +75,15 @@ impl<'a> Parser<'a> { } let expression = self.parse_expression()?; + + if self.eat_assign() { + if let Some(lvalue) = LValue::from_expression(expression.clone()) { + return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); + } else { + // TODO: error (invalid l-value) + } + } + Some(StatementKind::Expression(expression)) } @@ -233,7 +242,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ConstrainKind, ExpressionKind, ForRange, StatementKind, UnresolvedTypeData}, + ast::{ConstrainKind, ExpressionKind, ForRange, LValue, StatementKind, UnresolvedTypeData}, parser::{ parser::tests::{get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, @@ -406,4 +415,19 @@ mod tests { assert_eq!(for_loop.identifier.to_string(), "i"); assert!(matches!(for_loop.range, ForRange::Array(..))); } + + #[test] + fn parses_assignment() { + let src = "x = 1"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + 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"); + } } From 0115c28b434749be50fa69d23b22018ce26acb32 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 16:47:06 -0300 Subject: [PATCH 115/229] Parse op assign --- .../noirc_frontend/src/parser/parser/infix.rs | 4 +- .../src/parser/parser/statement.rs | 86 ++++++++++++++++++- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index f811061b8dc..338292a111d 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -129,7 +129,9 @@ impl<'a> Parser<'a> { BinaryOpKind::Less } else if self.eat(Token::LessEqual) { BinaryOpKind::LessEqual - } else if self.eat(Token::Greater) { + } else + // Make sure to skip the `>>=` case, as `>>=` is lexed as `> >=`. + if self.next_token.token() != &Token::GreaterEqual && self.eat(Token::Greater) { BinaryOpKind::Greater } else if self.eat(Token::GreaterEqual) { BinaryOpKind::GreaterEqual diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 80985fce719..7823d5ae61b 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -1,9 +1,10 @@ -use noirc_errors::Span; +use noirc_errors::{Span, Spanned}; use crate::{ ast::{ - AssignStatement, ConstrainKind, ConstrainStatement, Expression, ExpressionKind, - ForLoopStatement, ForRange, Ident, LValue, LetStatement, Statement, StatementKind, + AssignStatement, BinaryOp, BinaryOpKind, ConstrainKind, ConstrainStatement, Expression, + ExpressionKind, ForLoopStatement, ForRange, Ident, InfixExpression, LValue, LetStatement, + Statement, StatementKind, }, parser::ParserErrorReason, token::{Attribute, Keyword, Token, TokenKind}, @@ -40,6 +41,8 @@ impl<'a> Parser<'a> { &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) => { @@ -78,6 +81,26 @@ impl<'a> Parser<'a> { 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 { + // TODO: error (invalid l-value) + } + } + + 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 { // TODO: error (invalid l-value) @@ -87,6 +110,38 @@ impl<'a> Parser<'a> { Some(StatementKind::Expression(expression)) } + fn next_is_op_assign(&mut self) -> Option { + let start_span = self.current_token_span; + let operator = if self.next_token.token() == &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.token.token() == &Token::Greater + && self.next_token.token() == &Token::GreaterEqual + { + Some(BinaryOpKind::ShiftRight) + } else { + None + }; + + if let Some(operator) = operator { + self.next_token(); + self.next_token(); + Some(Spanned::from(self.span_since(start_span), operator)) + } else { + None + } + } + fn parse_for(&mut self) -> Option { let start_span = self.current_token_span; @@ -429,5 +484,30 @@ mod tests { panic!("Expected ident"); }; assert_eq!(ident.to_string(), "x"); + assert_eq!(assign.expression.to_string(), "1"); + } + + #[test] + fn parses_op_assignment() { + let src = "x += 1"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + let StatementKind::Assign(assign) = statement.kind else { + panic!("Expected assign"); + }; + assert_eq!(assign.to_string(), "x = (x >> 1)"); } } From b114fc0d2a8c0746bffc8486641a1ccccf972437 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 16:55:03 -0300 Subject: [PATCH 116/229] One look-ahead token is enough --- compiler/noirc_frontend/src/parser/parser.rs | 14 ++++---------- compiler/noirc_frontend/src/parser/parser/infix.rs | 1 - 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 830c6257e6a..e9444392946 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -79,13 +79,11 @@ pub struct Parser<'a> { errors: Vec, tokens: TokenStream<'a>, - // We always have two look-ahead tokens for these cases: + // We always have one look-ahead token for these cases: // - check if we get `&` or `&mut` // - check if we get `>` or `>>` - // - check if we get `>>` or `>>=` token: SpannedToken, next_token: SpannedToken, - next_next_token: SpannedToken, current_token_span: Span, previous_token_span: Span, } @@ -110,11 +108,10 @@ impl<'a> Parser<'a> { tokens, token: SpannedToken::default(), next_token: SpannedToken::default(), - next_next_token: SpannedToken::default(), current_token_span: Default::default(), previous_token_span: Default::default(), }; - parser.read_three_first_tokens(); + parser.read_two_first_tokens(); parser } @@ -133,18 +130,15 @@ impl<'a> Parser<'a> { self.previous_token_span = self.current_token_span; let token = self.read_token_internal(); let next_token = std::mem::take(&mut self.next_token); - let next_next_token = std::mem::take(&mut self.next_next_token); self.token = next_token; - self.next_token = next_next_token; - self.next_next_token = token; + self.next_token = token; self.current_token_span = self.token.to_span(); } - fn read_three_first_tokens(&mut self) { + 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(); - self.next_next_token = self.read_token_internal(); } fn read_token_internal(&mut self) -> SpannedToken { diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 338292a111d..9f40ba269e4 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -165,7 +165,6 @@ impl<'a> Parser<'a> { // parse two greater-than tokens as a single right-shift here. if self.token.token() == &Token::Greater && self.next_token.token() == &Token::Greater - && self.next_next_token.token() != &Token::Assign { self.next_token(); self.next_token(); From 7ff294098b3e0a30176378172f876d6911f04b24 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 18:25:32 -0300 Subject: [PATCH 117/229] Some refactors --- compiler/noirc_frontend/src/parser/parser.rs | 4 ++++ .../src/parser/parser/expression.rs | 9 ++------- .../noirc_frontend/src/parser/parser/function.rs | 7 +------ .../noirc_frontend/src/parser/parser/impls.rs | 6 +----- .../noirc_frontend/src/parser/parser/lambda.rs | 16 +++------------- .../src/parser/parser/statement.rs | 5 +---- .../noirc_frontend/src/parser/parser/types.rs | 13 ++++++------- 7 files changed, 18 insertions(+), 42 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index e9444392946..03f2e86f9a3 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -400,6 +400,10 @@ impl<'a> Parser<'a> { } } + fn span_at_previous_token_end(&self) -> Span { + Span::from(self.previous_token_span.end()..self.previous_token_span.end()) + } + fn push_error(&mut self, reason: ParserErrorReason, span: Span) { self.errors.push(ParserError::with_reason(reason, span)); } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index e1a2c13db71..ec85dd2c84c 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,5 +1,3 @@ -use noirc_errors::Span; - use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, @@ -36,10 +34,7 @@ impl<'a> Parser<'a> { expr } else { self.push_expected_expression_after_this_error(); - Expression { - kind: ExpressionKind::Error, - span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), - } + Expression { kind: ExpressionKind::Error, span: self.span_at_previous_token_end() } } } @@ -349,7 +344,7 @@ impl<'a> Parser<'a> { ParserErrorReason::ExpectedLeftBraceAfterIfCondition, self.current_token_span, ); - let span = Span::from(self.previous_token_span.end()..self.previous_token_span.end()); + let span = self.span_at_previous_token_end(); return Some(ExpressionKind::If(Box::new(IfExpression { condition, consequence: Expression { kind: ExpressionKind::Error, span }, diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index d67c6ae9286..4c36e28744d 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -91,12 +91,7 @@ impl<'a> Parser<'a> { let visibility = self.parse_visibility(); (FunctionReturnType::Ty(self.parse_type_or_error()), visibility) } else { - ( - FunctionReturnType::Default(Span::from( - self.previous_token_span.end()..self.previous_token_span.end(), - )), - Visibility::Private, - ) + (FunctionReturnType::Default(self.span_at_previous_token_end()), Visibility::Private) }; let where_clause = self.parse_where_clause(); diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index a60cb85b51a..135850d46d1 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -220,11 +220,7 @@ impl<'a> Parser<'a> { } }; - let typ = if self.eat_colon() { - self.parse_type_or_error() - } else { - UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } - }; + let typ = self.parse_optional_type_annotation(); let expr = if self.eat_assign() { self.parse_expression_or_error() diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index 9e483d22de8..4743d7ae252 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -1,7 +1,5 @@ -use noirc_errors::Span; - use crate::{ - ast::{ExpressionKind, Lambda, Pattern, UnresolvedType, UnresolvedTypeData}, + ast::{ExpressionKind, Lambda, Pattern, UnresolvedType}, parser::ParserErrorReason, token::Token, }; @@ -18,9 +16,7 @@ impl<'a> Parser<'a> { let return_type = if self.eat(Token::Arrow) { self.parse_type_or_error() } else { - UnresolvedTypeData::Unspecified.with_span(Span::from( - self.previous_token_span.end()..self.previous_token_span.end(), - )) + self.unspecified_type_at_previous_token_end() }; let body = self.parse_expression_or_error(); @@ -52,13 +48,7 @@ impl<'a> Parser<'a> { self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); } - let typ = if self.eat_colon() { - self.parse_type_or_error() - } else { - UnresolvedTypeData::Unspecified.with_span(Span::from( - self.previous_token_span.end()..self.previous_token_span.end(), - )) - }; + let typ = self.parse_optional_type_annotation(); parameters.push((pattern, typ)); trailing_comma = self.eat_commas(); diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 7823d5ae61b..d16558a4260 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -21,10 +21,7 @@ impl<'a> Parser<'a> { ParserErrorReason::ExpectedStatementAfterThis, self.previous_token_span, ); - Statement { - kind: StatementKind::Error, - span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), - } + Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() } } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 75c3fcf888b..624a20a5d49 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,5 +1,3 @@ -use noirc_errors::Span; - use crate::{ ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::ParserErrorReason, @@ -15,10 +13,7 @@ impl<'a> Parser<'a> { typ } else { self.push_error(ParserErrorReason::ExpectedTypeAfterThis, self.previous_token_span); - UnresolvedType { - typ: UnresolvedTypeData::Error, - span: Span::from(self.previous_token_span.end()..self.previous_token_span.end()), - } + self.unspecified_type_at_previous_token_end() } } @@ -310,9 +305,13 @@ impl<'a> Parser<'a> { if self.eat_colon() { self.parse_type_or_error() } else { - UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + 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)] From e6ebd5b1a0847a95f4ae69189d238f5c0c000d6a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 18:41:37 -0300 Subject: [PATCH 118/229] Parse function type --- .../noirc_frontend/src/parser/parser/types.rs | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 624a20a5d49..7d921f0080d 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -73,6 +73,10 @@ impl<'a> Parser<'a> { return Some(typ); } + if let Some(typ) = self.parse_function_type() { + return Some(typ); + } + if let Some(typ) = self.parse_resolved_type() { return Some(typ); } @@ -186,6 +190,76 @@ impl<'a> Parser<'a> { None } + fn parse_function_type(&mut self) -> Option { + let unconstrained = self.eat_keyword(Keyword::Unconstrained); + + if !self.eat_keyword(Keyword::Fn) { + if unconstrained { + // TODO: error (expected `fn` after `unconstrained`) + 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; + } + + let env = if self.eat_left_bracket() { + let typ = self.parse_type_or_error(); + if !self.eat_right_bracket() { + // TODO: error (expected `[` after `fn` env) + } + typ + } else { + self.unspecified_type_at_previous_token_end() + }; + + if !self.eat_left_paren() { + // TODO: error (expected `(` after `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, + )); + } + + let mut args = Vec::new(); + let mut trailing_comma = false; + + loop { + if self.eat_right_paren() { + break; + } + + let start_span = self.current_token_span; + let typ = self.parse_type_or_error(); + if let UnresolvedTypeData::Unspecified = typ.typ { + self.eat_right_paren(); + break; + } + + if !trailing_comma && !args.is_empty() { + self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); + } + + args.push(typ); + + trailing_comma = self.eat_commas(); + } + + let ret = if self.eat(Token::Arrow) { + self.parse_type_or_error() + } else { + self.unspecified_type_at_previous_token_end() + }; + + Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) + } + fn parse_resolved_type(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::QuotedType) { match token.into_token() { @@ -503,4 +577,72 @@ mod tests { assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); assert_eq!(expr.to_string(), "10"); } + + #[test] + fn parses_empty_function_type() { + let src = "fn()"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeData::Function(args, ret, env, unconstrained) = typ.typ else { + panic!("Expected a function type") + }; + assert!(args.is_empty()); + assert!(matches!(ret.typ, UnresolvedTypeData::Unspecified)); + assert!(matches!(env.typ, UnresolvedTypeData::Unspecified)); + assert!(!unconstrained); + } + + #[test] + fn parses_function_type_with_arguments() { + let src = "fn(Field, bool)"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + 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"); + assert!(matches!(ret.typ, UnresolvedTypeData::Unspecified)); + assert!(matches!(env.typ, UnresolvedTypeData::Unspecified)); + assert!(!unconstrained); + } + + #[test] + fn parses_function_type_with_return_type() { + let src = "fn() -> Field"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + 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]()"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + 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()"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeData::Function(_args, _ret, _env, unconstrained) = typ.typ else { + panic!("Expected a function type") + }; + assert!(unconstrained); + } } From 6315eeb303d8bcb0818a8e16b7b7ed3294668eca Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 18:49:40 -0300 Subject: [PATCH 119/229] Function type fixes --- .../noirc_frontend/src/parser/parser/types.rs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 7d921f0080d..b1d52c752e3 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -214,7 +214,7 @@ impl<'a> Parser<'a> { } typ } else { - self.unspecified_type_at_previous_token_end() + UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) }; if !self.eat_left_paren() { @@ -254,7 +254,8 @@ impl<'a> Parser<'a> { let ret = if self.eat(Token::Arrow) { self.parse_type_or_error() } else { - self.unspecified_type_at_previous_token_end() + // TODO: error (expected `->` after function type arguments) + UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) }; Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) @@ -580,7 +581,7 @@ mod tests { #[test] fn parses_empty_function_type() { - let src = "fn()"; + let src = "fn() -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); @@ -588,26 +589,23 @@ mod tests { panic!("Expected a function type") }; assert!(args.is_empty()); - assert!(matches!(ret.typ, UnresolvedTypeData::Unspecified)); - assert!(matches!(env.typ, UnresolvedTypeData::Unspecified)); + 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)"; + let src = "fn(Field, bool) -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); - let UnresolvedTypeData::Function(args, ret, env, unconstrained) = typ.typ else { + 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"); - assert!(matches!(ret.typ, UnresolvedTypeData::Unspecified)); - assert!(matches!(env.typ, UnresolvedTypeData::Unspecified)); - assert!(!unconstrained); } #[test] @@ -624,7 +622,7 @@ mod tests { #[test] fn parses_function_type_with_env() { - let src = "fn[Field]()"; + let src = "fn[Field]() -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); @@ -636,7 +634,7 @@ mod tests { #[test] fn parses_unconstrained_function_type() { - let src = "unconstrained fn()"; + let src = "unconstrained fn() -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); From 32f1f6ba888d42bfb918b845977ec25afb2cd74b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 18:54:14 -0300 Subject: [PATCH 120/229] Parse trait as type --- .../noirc_frontend/src/parser/parser/types.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index b1d52c752e3..7dbb02160a4 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -77,6 +77,10 @@ impl<'a> Parser<'a> { return Some(typ); } + if let Some(typ) = self.parse_trait_as_type() { + return Some(typ); + } + if let Some(typ) = self.parse_resolved_type() { return Some(typ); } @@ -261,6 +265,17 @@ impl<'a> Parser<'a> { Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) } + fn parse_trait_as_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::Impl) { + return None; + } + + let path = self.parse_path_no_turbofish(); + let generics = self.parse_generic_type_args(); + + Some(UnresolvedTypeData::TraitAsType(path, generics)) + } + fn parse_resolved_type(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::QuotedType) { match token.into_token() { @@ -643,4 +658,17 @@ mod tests { }; assert!(unconstrained); } + + #[test] + fn parses_trait_as_type_no_generics() { + let src = "impl foo::Bar"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeData::TraitAsType(path, generics) = typ.typ else { + panic!("Expected trait as type") + }; + assert_eq!(path.to_string(), "foo::Bar"); + assert!(generics.is_empty()); + } } From 5d63cdcd4b1bd85266929776921157c2c479d36b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 19:15:52 -0300 Subject: [PATCH 121/229] Parse AsTraitPath --- .../noirc_frontend/src/parser/parser/path.rs | 29 ++++++++++++++++++- .../noirc_frontend/src/parser/parser/types.rs | 29 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 6b55e643440..4a80c89c743 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,7 +1,7 @@ use noirc_errors::Span; use crate::{ - ast::{Ident, Path, PathKind, PathSegment, UnresolvedType}, + ast::{AsTraitPath, Ident, Path, PathKind, PathSegment, UnresolvedType}, parser::ParserErrorReason, token::{Keyword, TokenKind}, }; @@ -140,6 +140,33 @@ impl<'a> Parser<'a> { PathKind::Plain } } + + pub(super) fn parse_as_trait_path(&mut self) -> Option { + if !self.eat_less() { + return None; + } + + let typ = self.parse_type_or_error(); + if !self.eat_keyword(Keyword::As) { + // TODO: error (expected `as`) + } + let trait_path = self.parse_path_no_turbofish(); + let trait_generics = self.parse_generic_type_args(); + if !self.eat_greater() { + // TODO: error (expected `>`) + } + if !self.eat_double_colon() { + // TODO: error (expected `::`) + } + let impl_item = if let Some(ident) = self.eat_ident() { + ident + } else { + // TODO: error (expected identifier) + Ident::new(String::new(), self.span_at_previous_token_end()) + }; + + Some(AsTraitPath { typ, trait_path, trait_generics, impl_item }) + } } #[cfg(test)] diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 7dbb02160a4..fb5a7f01f75 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -81,6 +81,10 @@ impl<'a> Parser<'a> { return Some(typ); } + if let Some(typ) = self.parse_as_trait_path_type() { + return Some(typ); + } + if let Some(typ) = self.parse_resolved_type() { return Some(typ); } @@ -271,11 +275,21 @@ impl<'a> Parser<'a> { } let path = self.parse_path_no_turbofish(); + if path.is_empty() { + // TODO: error (expected path after impl) + return None; + } + let generics = self.parse_generic_type_args(); 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() { @@ -671,4 +685,19 @@ mod tests { assert_eq!(path.to_string(), "foo::Bar"); assert!(generics.is_empty()); } + + #[test] + fn parses_as_trait_path() { + let src = "::baz"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + 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"); + } } From 1a9f62adfda1758149f9f37d1edf9111eaa773e0 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 19:15:58 -0300 Subject: [PATCH 122/229] Fix parsing of negative literals --- .../src/parser/parser/expression.rs | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index ec85dd2c84c..02d0cebe29d 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -2,8 +2,7 @@ use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, Literal, - MemberAccessExpression, MethodCallExpression, Path, PrefixExpression, UnaryOp, - UnresolvedType, + MemberAccessExpression, MethodCallExpression, Path, UnaryOp, UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, @@ -52,7 +51,7 @@ impl<'a> Parser<'a> { ); return None; }; - let kind = ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })); + let kind = ExpressionKind::prefix(operator, rhs); let span = self.span_since(start_span); return Some(Expression { kind, span }); } @@ -213,9 +212,12 @@ impl<'a> Parser<'a> { // TODO: parse these too // comptime_expr(statement.clone()), // unquote(expr_parser.clone()), - // as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), // type_path(parse_type()), + 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); } @@ -600,6 +602,19 @@ mod tests { assert!(!negative); } + #[test] + fn parses_negative_integer_literal() { + let src = "-42"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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)"; @@ -1262,4 +1277,19 @@ mod tests { 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 mut parser = Parser::for_str(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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"); + } } From c71c86d2d7a690bf5ac9a4ce7d79b1ff8bcfea30 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 21:13:08 -0300 Subject: [PATCH 123/229] Parse format string type --- .../noirc_frontend/src/parser/parser/types.rs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index fb5a7f01f75..7e6f3436b20 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -69,6 +69,10 @@ impl<'a> Parser<'a> { return Some(typ); } + if let Some(typ) = self.parse_fmtstr_type() { + return Some(typ); + } + if let Some(typ) = self.parse_comptime_type() { return Some(typ); } @@ -155,6 +159,39 @@ impl<'a> Parser<'a> { Some(UnresolvedTypeData::String(expr)) } + fn parse_fmtstr_type(&mut self) -> Option { + if !self.eat_keyword(Keyword::FormatString) { + return None; + } + + if !self.eat_less() { + self.push_error(ParserErrorReason::ExpectedStringTypeLength, self.current_token_span); + 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))); + } + + let expr = match self.parse_type_expression() { + Ok(expr) => expr, + Err(error) => { + self.errors.push(error); + UnresolvedTypeExpression::Constant(0, self.current_token_span) + } + }; + + if !self.eat_commas() { + // TODO: error (expected comma after fmtstr type expression) + } + + let typ = self.parse_type_or_error(); + + if !self.eat_greater() { + // TODO: error (expected closing `>`) + } + + 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)); @@ -480,6 +517,19 @@ mod tests { assert_eq!(expr.to_string(), "10"); } + #[test] + fn parses_fmtstr_type() { + let src = "fmtstr<10, T>"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_error(); + assert!(parser.errors.is_empty()); + 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() { From d0827b8ea2cfdd78327a50776b742d87dbf90923 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 21:17:49 -0300 Subject: [PATCH 124/229] Parse comptime expr --- .../src/parser/parser/expression.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 02d0cebe29d..08735cf6ac8 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -209,6 +209,10 @@ impl<'a> Parser<'a> { return Some(kind); } + if let Some(kind) = self.parse_comptime_expr() { + return Some(kind); + } + // TODO: parse these too // comptime_expr(statement.clone()), // unquote(expr_parser.clone()), @@ -377,6 +381,21 @@ impl<'a> Parser<'a> { Some(ExpressionKind::If(Box::new(IfExpression { condition, consequence, alternative }))) } + fn parse_comptime_expr(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.eat_keyword(Keyword::Comptime) { + return None; + } + + let Some(block) = self.parse_block_expression() else { + // TODO: error (expected `{` after comptime) + return None; + }; + + Some(ExpressionKind::Comptime(block, self.span_since(start_span))) + } + fn parse_literal(&mut self) -> Option { if let Some(bool) = self.eat_bool() { return Some(ExpressionKind::Literal(Literal::Bool(bool))); @@ -1292,4 +1311,16 @@ mod tests { 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 mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Comptime(block, _) = expr.kind else { + panic!("Expected comptime block"); + }; + assert_eq!(block.statements.len(), 1); + } } From ea39d9dfe4a31b2129f35bcfa631e91d99082cbf Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 21:30:24 -0300 Subject: [PATCH 125/229] Parse type path --- .../src/parser/parser/expression.rs | 71 ++++++++++++++++++- .../noirc_frontend/src/parser/parser/types.rs | 28 ++++---- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 08735cf6ac8..0c01c1b5523 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,8 +1,8 @@ use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, - Expression, ExpressionKind, Ident, IfExpression, IndexExpression, Literal, - MemberAccessExpression, MethodCallExpression, Path, UnaryOp, UnresolvedType, + Expression, ExpressionKind, GenericTypeArgs, Ident, IfExpression, IndexExpression, Literal, + MemberAccessExpression, MethodCallExpression, Path, TypePath, UnaryOp, UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, @@ -214,10 +214,13 @@ impl<'a> Parser<'a> { } // TODO: parse these too - // comptime_expr(statement.clone()), // unquote(expr_parser.clone()), // type_path(parse_type()), + 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)); } @@ -396,6 +399,40 @@ impl<'a> Parser<'a> { Some(ExpressionKind::Comptime(block, self.span_since(start_span))) } + 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) }; + + if !self.eat_double_colon() { + // TODO: error (expected `::` after type) + } + + let item = if let Some(ident) = self.eat_ident() { + ident + } else { + self.push_error( + ParserErrorReason::ExpectedIdentifierAfterColons, + self.current_token_span, + ); + 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() { + // TODO: error + } + generics + } else { + GenericTypeArgs::default() + }; + + Some(ExpressionKind::TypePath(TypePath { typ, item, turbofish })) + } + fn parse_literal(&mut self) -> Option { if let Some(bool) = self.eat_bool() { return Some(ExpressionKind::Literal(Literal::Bool(bool))); @@ -1323,4 +1360,32 @@ mod tests { }; assert_eq!(block.statements.len(), 1); } + + #[test] + fn parses_type_path() { + let src = "Field::foo"; + let mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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()); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 7e6f3436b20..146a27d8ed0 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -41,18 +41,28 @@ impl<'a> Parser<'a> { return Some(typ); } + if let Some(typ) = self.parse_function_type() { + return Some(typ); + } + + if let Some(typ) = self.parse_trait_as_type() { + return Some(typ); + } + + if let Some(typ) = self.parse_as_trait_path_type() { + return Some(typ); + } + let path = self.parse_path_no_turbofish(); if !path.is_empty() { let generics = self.parse_generic_type_args(); return Some(UnresolvedTypeData::Named(path, generics, false)); } - // TODO: parse more types - None } - fn parse_primitive_type(&mut self) -> Option { + pub(super) fn parse_primitive_type(&mut self) -> Option { if let Some(typ) = self.parse_field_type() { return Some(typ); } @@ -77,18 +87,6 @@ impl<'a> Parser<'a> { return Some(typ); } - if let Some(typ) = self.parse_function_type() { - return Some(typ); - } - - if let Some(typ) = self.parse_trait_as_type() { - return Some(typ); - } - - if let Some(typ) = self.parse_as_trait_path_type() { - return Some(typ); - } - if let Some(typ) = self.parse_resolved_type() { return Some(typ); } From 6037200027fcd787a6f9040203ad336610a05a82 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 21:58:56 -0300 Subject: [PATCH 126/229] Parse unquote expr --- .../src/parser/parser/expression.rs | 67 ++++++++++++++++++- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 0c01c1b5523..4c21ca338d8 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -213,9 +213,9 @@ impl<'a> Parser<'a> { return Some(kind); } - // TODO: parse these too - // unquote(expr_parser.clone()), - // type_path(parse_type()), + if let Some(kind) = self.parse_unquote_expr() { + return Some(kind); + } if let Some(kind) = self.parse_type_path_expr() { return Some(kind); @@ -399,6 +399,40 @@ impl<'a> Parser<'a> { Some(ExpressionKind::Comptime(block, self.span_since(start_span))) } + fn parse_unquote_expr(&mut self) -> Option { + let start_span = self.current_token_span; + + if !self.eat(Token::DollarSign) { + return None; + } + + let path = self.parse_path(); + if !path.is_empty() { + 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(); + if !self.eat_right_paren() { + // TODO: error (missing `)` after quoted expression) + } + let expr = Expression { + kind: ExpressionKind::Parenthesized(Box::new(expr)), + span: self.span_since(span_at_left_paren), + }; + return Some(ExpressionKind::Unquote(Box::new(expr))); + } + + // TODO: error (found $ but nothing to unquote) + + None + } + fn parse_type_path_expr(&mut self) -> Option { let start_span = self.current_token_span; let Some(typ) = self.parse_primitive_type() else { @@ -1388,4 +1422,31 @@ mod tests { assert_eq!(type_path.item.to_string(), "foo"); assert!(!type_path.turbofish.is_empty()); } + + #[test] + fn parses_unquote_var() { + let src = "$foo::bar"; + let mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(&src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Unquote(expr) = expr.kind else { + panic!("Expected unquote"); + }; + assert_eq!(expr.kind.to_string(), "((1 + 2))") + } } From 96b6cf3f3535094719be88011465ebfc42681ecd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 21:59:05 -0300 Subject: [PATCH 127/229] Fix path with turbofish parsing --- .../src/parser/parser/expression.rs | 9 +++++ .../noirc_frontend/src/parser/parser/path.rs | 36 +++++-------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 4c21ca338d8..a9399b04d5b 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -971,6 +971,15 @@ mod tests { assert_eq!(path.to_string(), "foo::bar"); } + #[test] + fn parses_variable_path_with_turbofish() { + let src = "foo::<9>"; + 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()); + } + #[test] fn parses_mutable_ref() { let src = "&mut foo"; diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 4a80c89c743..4a559f0da41 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -3,7 +3,7 @@ use noirc_errors::Span; use crate::{ ast::{AsTraitPath, Ident, Path, PathKind, PathSegment, UnresolvedType}, parser::ParserErrorReason, - token::{Keyword, TokenKind}, + token::{Keyword, Token, TokenKind}, }; use super::Parser; @@ -97,36 +97,16 @@ impl<'a> Parser<'a> { } pub(super) fn parse_path_generics(&mut self) -> Option> { - if !self.eat_less() { + if self.token.token() != &Token::Less { return None; - } - - let mut generics = Vec::new(); - let mut trailing_comma = false; - - if self.eat_greater() { - // TODO: error - } else { - loop { - let star_span = self.current_token_span; - let Some(typ) = self.parse_type() else { - self.eat_greater(); - break; - }; - - if !trailing_comma && !generics.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, star_span); - } - - generics.push(typ); - trailing_comma = self.eat_commas(); + }; - if self.eat_greater() { - break; - } - } + let generics = self.parse_generic_type_args(); + for (name, _typ) in &generics.named_args { + self.push_error(ParserErrorReason::AssociatedTypesNotAllowedInPaths, name.span()); } - Some(generics) + + Some(generics.ordered_args) } pub(super) fn parse_path_kind(&mut self) -> PathKind { From 2f3d022cfe4afc2671c1f21e005a613cf7f20eec Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 22:01:55 -0300 Subject: [PATCH 128/229] Use correct error message --- .../noirc_frontend/src/parser/parser/expression.rs | 4 +++- compiler/noirc_frontend/src/parser/parser/path.rs | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a9399b04d5b..d2d924fd83d 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -114,7 +114,9 @@ impl<'a> Parser<'a> { }; let generics = if self.eat_double_colon() { - let generics = self.parse_path_generics(); + let generics = self.parse_path_generics( + ParserErrorReason::AssociatedTypesNotAllowedInMethodCalls, + ); if generics.is_none() { // TODO: error (found `::` but not `::<...>`) } diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 4a559f0da41..dc5092ef4ef 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -52,7 +52,9 @@ impl<'a> Parser<'a> { let mut has_double_colon = self.eat_double_colon(); let generics = if has_double_colon && allow_turbofish { - if let Some(generics) = self.parse_path_generics() { + if let Some(generics) = self + .parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) + { has_double_colon = self.eat_double_colon(); Some(generics) } else { @@ -96,14 +98,17 @@ impl<'a> Parser<'a> { Path { segments, kind: PathKind::Plain, span: self.span_since(start_span) } } - pub(super) fn parse_path_generics(&mut self) -> Option> { + pub(super) fn parse_path_generics( + &mut self, + on_named_arg_error: ParserErrorReason, + ) -> Option> { if self.token.token() != &Token::Less { return None; }; let generics = self.parse_generic_type_args(); for (name, _typ) in &generics.named_args { - self.push_error(ParserErrorReason::AssociatedTypesNotAllowedInPaths, name.span()); + self.push_error(on_named_arg_error.clone(), name.span()); } Some(generics.ordered_args) From 3189527051565d731bff4fe8101990d5ba00691c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 22:09:47 -0300 Subject: [PATCH 129/229] Semicolon after type alias --- compiler/noirc_frontend/src/parser/parser/type_alias.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 4de8c27ee54..44380b5a6d2 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -19,6 +19,7 @@ impl<'a> Parser<'a> { let generics = self.parse_generics(); if !self.eat_assign() { + let span = self.span_since(start_span); self.eat_semicolons(); // TODO: error @@ -26,13 +27,17 @@ impl<'a> Parser<'a> { name, generics, typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span: self.span_since(start_span), + span, }; } let typ = self.parse_type_or_error(); + let span = self.span_since(start_span); + if !self.eat_semicolons() { + // TODO: error? (missing semicolon after type alias declaration) + } - NoirTypeAlias { name, generics, typ, span: self.span_since(start_span) } + NoirTypeAlias { name, generics, typ, span } } } From 997de9c650a29401fbd92cbbbddd7ac40a3bfd21 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 22:33:00 -0300 Subject: [PATCH 130/229] Make sure we parse expressions until end of string --- .../src/parser/parser/expression.rs | 112 +++++++++++++++++- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index d2d924fd83d..2649e085a22 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,8 +1,12 @@ +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, Path, TypePath, UnaryOp, UnresolvedType, + MemberAccessExpression, MethodCallExpression, Path, Statement, TypePath, UnaryOp, + UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, @@ -626,7 +630,7 @@ impl<'a> Parser<'a> { return None; } - let mut statements = Vec::new(); + let mut statements: Vec<(Statement, (Option, Span))> = Vec::new(); loop { if self.eat_right_brace() { @@ -639,15 +643,38 @@ impl<'a> Parser<'a> { break; }; - statements.push(statement); + let (token, span) = if self.token.token() == &Token::Semicolon { + let token = self.token.clone(); + self.next_token(); + let span = token.to_span(); + (Some(token.into_token()), span) + } else { + (None, self.previous_token_span) + }; + + statements.push((statement, (token, span))); // TODO: error if missing semicolon and statement requires one and is not the last one in the block self.eat_semicolons(); } + let statements = self.check_statements_require_semicolon(statements); + Some(BlockExpression { statements }) } + 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_after_this_error(&mut self) { self.push_error(ParserErrorReason::ExpectedExpressionAfterThis, self.previous_token_span); } @@ -673,6 +700,7 @@ mod tests { let src = "true"; 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()); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(true)))); @@ -686,6 +714,7 @@ mod tests { let src = "42"; 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()); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); @@ -699,6 +728,7 @@ mod tests { let src = "-42"; 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()); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); @@ -712,6 +742,7 @@ mod tests { let src = "(42)"; 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()); let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); @@ -728,6 +759,7 @@ mod tests { let src = "()"; 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()); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Unit))); } @@ -737,6 +769,7 @@ mod tests { let src = "\"hello\""; 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()); let ExpressionKind::Literal(Literal::Str(string)) = expr.kind else { panic!("Expected string literal"); @@ -749,6 +782,7 @@ mod tests { let src = "r#\"hello\"#"; 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()); let ExpressionKind::Literal(Literal::RawStr(string, n)) = expr.kind else { panic!("Expected raw string literal"); @@ -762,6 +796,7 @@ mod tests { let src = "f\"hello\""; 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()); let ExpressionKind::Literal(Literal::FmtStr(string)) = expr.kind else { panic!("Expected format string literal"); @@ -774,6 +809,7 @@ mod tests { let src = "(1, 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()); let ExpressionKind::Tuple(mut exprs) = expr.kind else { panic!("Expected tuple expression"); @@ -800,6 +836,7 @@ mod tests { let src = "{ 1 }"; 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()); let ExpressionKind::Block(mut block) = expr.kind else { panic!("Expected block expression"); @@ -825,10 +862,10 @@ mod tests { let x = 1; let y = 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!(parser.errors.is_empty()); let ExpressionKind::Block(block) = expr.kind else { panic!("Expected block expression"); @@ -839,11 +876,38 @@ mod tests { 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 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()); let ExpressionKind::Unsafe(block, _) = expr.kind else { panic!("Expected unsafe expression"); @@ -882,6 +946,7 @@ mod tests { let src = "[]"; 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()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -895,6 +960,7 @@ mod tests { let src = "[1]"; 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()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -909,6 +975,7 @@ mod tests { let src = "[1, 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!(parser.errors.is_empty()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -924,6 +991,7 @@ mod tests { let src = "[1; 10]"; 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()); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { repeated_element, @@ -941,6 +1009,7 @@ mod tests { let src = "&[]"; 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()); let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -954,6 +1023,7 @@ mod tests { let src = "foo"; 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()); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); @@ -966,6 +1036,7 @@ mod tests { let src = "foo::bar"; 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()); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); @@ -979,6 +1050,7 @@ mod tests { 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!(expr.span.end() as usize, src.len()); assert!(parser.errors.is_empty()); } @@ -987,6 +1059,7 @@ mod tests { let src = "&mut foo"; 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()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -1004,6 +1077,7 @@ mod tests { let src = "-foo"; 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()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -1021,6 +1095,7 @@ mod tests { let src = "!foo"; 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()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -1038,6 +1113,7 @@ mod tests { let src = "*foo"; 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()); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); @@ -1055,6 +1131,7 @@ mod tests { let src = "quote { 1 }"; 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()); let ExpressionKind::Quote(tokens) = expr.kind else { panic!("Expected quote expression"); @@ -1067,6 +1144,7 @@ mod tests { let src = "foo(1, 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()); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); @@ -1081,6 +1159,7 @@ mod tests { let src = "foo::(1, 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()); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); @@ -1095,6 +1174,7 @@ mod tests { let src = "foo!(1, 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()); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); @@ -1109,6 +1189,7 @@ mod tests { let src = "foo.bar"; 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()); let ExpressionKind::MemberAccess(member_access) = expr.kind else { panic!("Expected member access expression"); @@ -1122,6 +1203,7 @@ mod tests { let src = "foo.bar(1, 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()); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); @@ -1138,6 +1220,7 @@ mod tests { let src = "foo.bar::(1, 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()); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); @@ -1154,6 +1237,7 @@ mod tests { let src = "foo.bar!(1, 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()); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); @@ -1170,6 +1254,7 @@ mod tests { let src = "Foo {}"; 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()); let ExpressionKind::Constructor(constructor) = expr.kind else { panic!("Expected constructor"); @@ -1209,6 +1294,7 @@ mod tests { let src = "if true { 1 }"; 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()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1227,6 +1313,7 @@ mod tests { let src = "if foo { 1 }"; 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()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1239,6 +1326,7 @@ mod tests { let src = "if true { 1 } else { 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()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1252,6 +1340,7 @@ mod tests { let src = "if true { 1 } else if false { 2 } else { 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!(parser.errors.is_empty()); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); @@ -1267,6 +1356,7 @@ mod tests { let src = "1 as u8"; 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()); let ExpressionKind::Cast(cast_expr) = expr.kind else { panic!("Expected cast"); @@ -1293,6 +1383,7 @@ mod tests { let src = "1[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()); let ExpressionKind::Index(index_expr) = expr.kind else { panic!("Expected index"); @@ -1307,6 +1398,7 @@ mod tests { 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}"); @@ -1322,6 +1414,7 @@ mod tests { let src = "1 + 2 * 3 + 4"; 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()); let ExpressionKind::Infix(infix_expr) = expr.kind else { panic!("Expected infix"); @@ -1336,6 +1429,7 @@ mod tests { let src = "|| 1"; 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()); let ExpressionKind::Lambda(lambda) = expr.kind else { panic!("Expected lambda"); @@ -1350,6 +1444,7 @@ mod tests { let src = "|x, y: Field| 1"; 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()); let ExpressionKind::Lambda(mut lambda) = expr.kind else { panic!("Expected lambda"); @@ -1370,6 +1465,7 @@ mod tests { let src = "|| -> Field 1"; 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()); let ExpressionKind::Lambda(lambda) = expr.kind else { panic!("Expected lambda"); @@ -1384,6 +1480,7 @@ mod tests { let src = "::baz"; 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()); let ExpressionKind::AsTraitPath(as_trait_path) = expr.kind else { panic!("Expected as_trait_path") @@ -1399,6 +1496,7 @@ mod tests { let src = "comptime { 1 }"; 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()); let ExpressionKind::Comptime(block, _) = expr.kind else { panic!("Expected comptime block"); @@ -1411,6 +1509,7 @@ mod tests { let src = "Field::foo"; 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()); let ExpressionKind::TypePath(type_path) = expr.kind else { panic!("Expected type_path"); @@ -1425,6 +1524,7 @@ mod tests { let src = "Field::foo::"; 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()); let ExpressionKind::TypePath(type_path) = expr.kind else { panic!("Expected type_path"); @@ -1439,6 +1539,7 @@ mod tests { let src = "$foo::bar"; 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()); let ExpressionKind::Unquote(expr) = expr.kind else { panic!("Expected unquote"); @@ -1454,6 +1555,7 @@ mod tests { let src = "$(1 + 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()); let ExpressionKind::Unquote(expr) = expr.kind else { panic!("Expected unquote"); From b7fac937c3b99c803d3a6958d600861d092197fa Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 22:43:50 -0300 Subject: [PATCH 131/229] Parse attributes on impl method --- .../noirc_frontend/src/parser/parser/impls.rs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 135850d46d1..431fc11e92d 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -56,8 +56,8 @@ impl<'a> Parser<'a> { // TODO: maybe require visibility to always come first let doc_comments = self.parse_outer_doc_comments(); let start_span = self.current_token_span; + let attributes = self.parse_attributes(); let modifiers = self.parse_modifiers(); - let attributes = Vec::new(); if self.eat_keyword(Keyword::Fn) { let method = self.parse_function( @@ -73,7 +73,6 @@ impl<'a> Parser<'a> { break; } } else { - // TODO: parse Type and Constant // TODO: error if visibility, unconstrained or comptime were found if !self.eat_right_brace() { @@ -299,6 +298,25 @@ mod tests { assert_eq!(method.def.visibility, ItemVisibility::Public); } + #[test] + fn parse_impl_with_attribute_on_method() { + let src = " + impl Foo { + #[something] + fn foo(self) {} + } + "; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Impl(type_impl) = item.kind else { + panic!("Expected type impl"); + }; + 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) {} }"; From 7d47012159b7addf2464dec13902225a0379e986 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 22:46:28 -0300 Subject: [PATCH 132/229] Also parse attributes on trait impl function --- compiler/noirc_frontend/src/parser/parser/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 431fc11e92d..ba536a838c0 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -164,7 +164,7 @@ impl<'a> Parser<'a> { modifiers.visibility_span, ); } - let attributes = Vec::new(); + let attributes = self.parse_attributes(); if !self.eat_keyword(Keyword::Fn) { // TODO: error if unconstrained, visibility or comptime From 904011fec0bd8ea2c1d7042b60f50b6582308740 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 22:46:54 -0300 Subject: [PATCH 133/229] Recover from parsing non-item token --- compiler/noirc_frontend/src/parser/parser/item.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 526b219345c..21decf54c02 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -98,6 +98,8 @@ impl<'a> Parser<'a> { } // TODO: error + // We'll try parsing an item on the next token + self.next_token(); None } From 176fa02f1db318f58450b7b5d075f68db969d9f4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 23:07:46 -0300 Subject: [PATCH 134/229] Fix impl self type in method followed by colon type --- .../src/parser/parser/function.rs | 7 ++++-- .../noirc_frontend/src/parser/parser/impls.rs | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 4c36e28744d..a1a43fba229 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -190,8 +190,11 @@ impl<'a> Parser<'a> { let ident = Ident::new("self".to_string(), span); let path = Path::from_single("Self".to_owned(), span); let no_args = GenericTypeArgs::default(); - let mut self_type = - UnresolvedTypeData::Named(path, no_args, true).with_span(span); + let mut self_type = if self.eat_colon() { + self.parse_type_or_error() + } else { + UnresolvedTypeData::Named(path, no_args, true).with_span(span) + }; let mut pattern = Pattern::Identifier(ident); if self_pattern.reference { diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index ba536a838c0..64288872d10 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -396,6 +396,31 @@ mod tests { 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 module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Impl(mut type_impl) = item.kind else { + panic!("Expected type impl"); + }; + 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 {"; From edd7da3d0b8ffd5746a0465356e417d2cbd3bf4e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 23:08:01 -0300 Subject: [PATCH 135/229] Parse if and block statements --- .../src/parser/parser/expression.rs | 2 +- .../src/parser/parser/statement.rs | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 2649e085a22..132accbc460 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -346,7 +346,7 @@ impl<'a> Parser<'a> { })) } - fn parse_if_expr(&mut self) -> Option { + pub(super) fn parse_if_expr(&mut self) -> Option { if !self.eat_keyword(Keyword::If) { return None; } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index d16558a4260..e912b69acf7 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -74,6 +74,20 @@ impl<'a> Parser<'a> { 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_expression() { + return Some(StatementKind::Expression(Expression { + kind: ExpressionKind::Block(block), + span: self.span_since(start_span), + })); + } + let expression = self.parse_expression()?; if self.eat_assign() { @@ -507,4 +521,36 @@ mod tests { }; 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 mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + 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); + } } From 5a408097b5a50a3e0dfe2467b5e9352a36afe54f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 23:11:46 -0300 Subject: [PATCH 136/229] Parse return statement --- .../src/parser/parser/statement.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index e912b69acf7..e2e0642f31c 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -57,6 +57,12 @@ impl<'a> Parser<'a> { 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.token.token() == &Token::Keyword(Keyword::Let) { let let_statement = self.parse_let_statement(attributes)?; return Some(StatementKind::Let(let_statement)); @@ -553,4 +559,19 @@ mod tests { }; 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(&parser.errors, span); + assert!(matches!(reason, ParserErrorReason::EarlyReturn)); + } } From b7c1283fd6dc630db9077c5f6d344f83935426e9 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 28 Sep 2024 23:57:25 -0300 Subject: [PATCH 137/229] More fixes --- .../src/hir/comptime/interpreter/builtin.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 10 ++++++++-- .../src/parser/parser/generics.rs | 18 ++++++++++-------- .../noirc_frontend/src/parser/parser/impls.rs | 14 ++++++++++++++ .../noirc_frontend/src/parser/parser/path.rs | 16 ---------------- .../noirc_frontend/src/parser/parser/types.rs | 10 +--------- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 2997defc1bd..a6b1e5af6f4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -690,7 +690,7 @@ fn quoted_as_expr( return option(return_type, Some(Value::statement(stmt.kind))); } - let result = parse(interner, argument, Parser::parse_lvalue, "an expression"); + 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))); } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 03f2e86f9a3..3e409e3cf2a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -122,8 +122,14 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } - pub(crate) fn parse_lvalue(&mut self) -> LValue { - todo!("Parser") + pub(crate) fn parse_lvalue_or_error(&mut self) -> LValue { + let expr = self.parse_expression_or_error(); + if let Some(lvalue) = LValue::from_expression(expr) { + lvalue + } else { + // TODO: error (invalid l-value) + LValue::Ident(Ident::default()) + } } fn next_token(&mut self) { diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index fc41aa604fd..f38f706ee05 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -123,18 +123,20 @@ impl<'a> Parser<'a> { let mut trailing_comma = false; loop { let start_span = self.current_token_span; - if let Some(ident) = self.eat_ident() { + + if matches!(self.token.token(), Token::Ident(..)) + && self.next_token.token() == &Token::Assign + { + let ident = self.eat_ident().unwrap(); + if !trailing_comma && !generic_type_args.is_empty() { self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); } - if self.eat_assign() { - let typ = self.parse_type_or_error(); - generic_type_args.named_args.push((ident, typ)); - } else { - let typ = self.parse_path_type_after_ident(ident); - generic_type_args.ordered_args.push(typ); - } + self.eat_assign(); + + let typ = self.parse_type_or_error(); + generic_type_args.named_args.push((ident, typ)); } else { let typ = self.parse_type(); let typ = typ.or_else(|| { diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 64288872d10..3742609e8b7 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -556,4 +556,18 @@ mod tests { assert_eq!(typ.to_string(), "Field"); assert_eq!(expr.to_string(), "1"); } + + #[test] + fn parse_trait_impl_with_generic_arithmetic() { + let src = "impl Foo for Field {}"; + let (mut module, errors) = parse_program(src); + assert!(errors.is_empty()); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + assert_eq!(item.span.end() as usize, src.len()); + let ItemKind::TraitImpl(trait_impl) = item.kind else { + panic!("Expected trait impl"); + }; + dbg!(trait_impl.trait_generics); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index dc5092ef4ef..c7070581288 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -82,22 +82,6 @@ impl<'a> Parser<'a> { (Path { segments, kind, span }, trailing_double_colon) } - pub(super) fn parse_path_no_turbofish_after_ident(&mut self, ident: Ident) -> Path { - let start_span = ident.span(); - let mut segments = vec![PathSegment::from(ident)]; - - while self.eat_double_colon() { - if let Some(ident) = self.eat_ident() { - segments.push(PathSegment::from(ident)); - } else { - // TODO: error (trailing double colon in path) - break; - } - } - - Path { segments, kind: PathKind::Plain, span: self.span_since(start_span) } - } - pub(super) fn parse_path_generics( &mut self, on_named_arg_error: ParserErrorReason, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 146a27d8ed0..a6090d8bf0a 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, + ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, QuotedType, @@ -432,14 +432,6 @@ impl<'a> Parser<'a> { }) } - pub(super) fn parse_path_type_after_ident(&mut self, ident: Ident) -> UnresolvedType { - let start_span = ident.span(); - let path = self.parse_path_no_turbofish_after_ident(ident); - let generics = self.parse_generic_type_args(); - let typ = UnresolvedTypeData::Named(path, generics, false); - UnresolvedType { typ, span: self.span_since(start_span) } - } - pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { if self.eat_colon() { self.parse_type_or_error() From 6cf0423cfdb23f3353675f22a40fb21d108a6dd0 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 06:45:56 -0300 Subject: [PATCH 138/229] Parse path stop before trailing double colon --- .../noirc_frontend/src/parser/parser/path.rs | 76 +++++++++++-------- .../src/parser/parser/use_tree.rs | 10 ++- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index c7070581288..5cce9a7abde 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -10,22 +10,18 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_path(&mut self) -> Path { - let (path, trailing_double_colon) = self.parse_path_impl(true); - if trailing_double_colon { - // TODO: error - } - path + self.parse_path_impl( + true, // allow turbofish + ) } pub(crate) fn parse_path_no_turbofish(&mut self) -> Path { - let (path, trailing_double_colon) = self.parse_path_impl(false); - if trailing_double_colon { - // TODO: error - } - path + self.parse_path_impl( + false, // allow turbofish + ) } - pub(super) fn parse_path_impl(&mut self, allow_turbofish: bool) -> (Path, bool) { + pub(super) fn parse_path_impl(&mut self, allow_turbofish: bool) -> Path { let start_span = self.current_token_span; let kind = self.parse_path_kind(); @@ -41,35 +37,32 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, start_span: Span, - ) -> (Path, bool) { - let mut trailing_double_colon = false; + ) -> Path { let mut segments = Vec::new(); if self.token.kind() == TokenKind::Ident { - while let Some(ident) = self.eat_ident() { + loop { + let ident = self.eat_ident().unwrap(); let span = ident.span(); - let mut has_double_colon = self.eat_double_colon(); - - let generics = if has_double_colon && allow_turbofish { - if let Some(generics) = self - .parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) - { - has_double_colon = self.eat_double_colon(); - Some(generics) - } else { - None - } + let generics = if allow_turbofish + && self.token.token() == &Token::DoubleColon + && self.next_token.token() == &Token::Less + { + self.next_token(); + self.parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) } else { None }; segments.push(PathSegment { ident, generics, span }); - if has_double_colon { - trailing_double_colon = true; + if self.token.token() == &Token::DoubleColon + && matches!(self.next_token.token(), Token::Ident(..)) + { + // Skip the double colons + self.next_token(); } else { - trailing_double_colon = false; break; } } @@ -77,9 +70,7 @@ impl<'a> Parser<'a> { // TODO: error } - let span = self.span_since(start_span); - - (Path { segments, kind, span }, trailing_double_colon) + Path { segments, kind, span: self.span_since(start_span) } } pub(super) fn parse_path_generics( @@ -174,6 +165,7 @@ mod tests { let src = "crate::foo::bar"; let mut parser = Parser::for_str(src); let path = parser.parse_path(); + dbg!(path.to_string()); assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Crate); assert_eq!(path.segments.len(), 2); @@ -216,12 +208,12 @@ mod tests { let src = "foo::"; let mut parser = Parser::for_str(src); let path = parser.parse_path(); + assert_eq!(path.span.end() as usize, src.len() - 2); assert_eq!(parser.errors.len(), 0); // TODO: this should be 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()); - assert_eq!(path.span.end() as usize, src.len()); } #[test] @@ -240,4 +232,24 @@ mod tests { 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(); + assert_eq!(path.span.end() as usize, src.len() - 2); + assert!(parser.errors.is_empty()); + 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(); + assert_eq!(path.span.end() as usize, src.len() - 2); + assert!(parser.errors.is_empty()); + assert_eq!(path.to_string(), "foo::bar::<1>"); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index a6bca2b43d3..1186bd4be09 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -28,14 +28,16 @@ impl<'a> Parser<'a> { start_span: Span, kind: PathKind, ) -> UseTree { - let (prefix, mut trailing_double_colon) = self.parse_path_after_kind( + let prefix = self.parse_path_after_kind( kind, false, // allow turbofish start_span, ); - if prefix.segments.is_empty() && kind != PathKind::Plain { - trailing_double_colon = true; - } + 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() { From f6226d5d017e48eb45884efa8b46a9be8daac29d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 06:54:57 -0300 Subject: [PATCH 139/229] More fixes --- .../src/parser/parser/generics.rs | 37 +++++++++++++++---- .../noirc_frontend/src/parser/parser/impls.rs | 14 ------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index f38f706ee05..c737cbb2bc5 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -3,7 +3,7 @@ use noirc_errors::Span; use crate::{ ast::{ GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UnresolvedTypeData, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, @@ -138,18 +138,23 @@ impl<'a> Parser<'a> { let typ = self.parse_type_or_error(); generic_type_args.named_args.push((ident, typ)); } else { - let typ = self.parse_type(); - let typ = typ.or_else(|| { - if let Ok(type_expr) = self.parse_type_expression() { - let span = type_expr.span(); + let typ = if let Ok(type_expr) = self.parse_type_expression() { + let span = type_expr.span(); + if let UnresolvedTypeExpression::Variable(path) = type_expr { + let generics = self.parse_generic_type_args(); Some(UnresolvedType { - typ: UnresolvedTypeData::Expression(type_expr), + typ: UnresolvedTypeData::Named(path, generics, false), span, }) } else { - None + Some(UnresolvedType { + typ: UnresolvedTypeData::Expression(type_expr), + span, + }) } - }); + } else { + self.parse_type() + }; let Some(typ) = typ else { // TODO: error? (not sure if this is `<>` so test that) @@ -278,4 +283,20 @@ mod tests { let reason = get_single_error(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); } + + #[test] + fn parse_arithmetic_generic_on_variable() { + let src = ""; + let mut parser = Parser::for_str(src); + let generics = parser.parse_generic_type_args(); + assert_eq!(generics.ordered_args[0].to_string(), "(N - 1)"); + } + + #[test] + fn parse_var_with_turbofish_in_generic() { + let src = ">"; + let mut parser = Parser::for_str(src); + let generics = parser.parse_generic_type_args(); + assert_eq!(generics.ordered_args[0].to_string(), "N<1>"); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 3742609e8b7..64288872d10 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -556,18 +556,4 @@ mod tests { assert_eq!(typ.to_string(), "Field"); assert_eq!(expr.to_string(), "1"); } - - #[test] - fn parse_trait_impl_with_generic_arithmetic() { - let src = "impl Foo for Field {}"; - let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); - assert_eq!(module.items.len(), 1); - let item = module.items.remove(0); - assert_eq!(item.span.end() as usize, src.len()); - let ItemKind::TraitImpl(trait_impl) = item.kind else { - panic!("Expected trait impl"); - }; - dbg!(trait_impl.trait_generics); - } } From de5106f35d6e800cb4478e876c808a0ae3146020 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 07:16:46 -0300 Subject: [PATCH 140/229] More type expressions --- .../src/parser/parser/type_expression.rs | 74 ++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 79b2e39b990..83e10505cb6 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -89,11 +89,40 @@ impl<'a> Parser<'a> { } 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_after_this_error(); + None + } + }; + } + + self.parse_atom_type_expression() + } + + 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.parses_variable_type_expression() { + 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); } @@ -122,7 +151,7 @@ impl<'a> Parser<'a> { Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) } - fn parses_variable_type_expression(&mut self) -> Option { + fn parse_variable_type_expression(&mut self) -> Option { let path = self.parse_path(); if path.is_empty() { None @@ -131,6 +160,26 @@ impl<'a> Parser<'a> { } } + fn parse_parenthesized_type_expression(&mut self) -> Option { + if !self.eat_left_paren() { + return None; + } + + return match self.parse_type_expression() { + Ok(type_expr) => { + if !self.eat_right_paren() { + // TODO: error (expected `)`) + } + Some(type_expr) + } + Err(error) => { + self.errors.push(error); + self.eat_right_paren(); + None + } + }; + } + fn expected_type_expression_after_this( &mut self, ) -> Result { @@ -182,4 +231,25 @@ mod tests { assert_eq!(operator, BinaryTypeOperator::Addition); assert_eq!(rhs.to_string(), "4"); } + + #[test] + fn parses_parenthesized_type_expression() { + let src = "(N)"; + let mut parser = Parser::for_str(src); + let expr = parser.parse_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let expr = parser.parse_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + assert_eq!(expr.to_string(), "(0 - N)"); + } } From 4f6286f0d384351ef763f446fb67c19f6571c37f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 07:21:49 -0300 Subject: [PATCH 141/229] Don't parse `()` in parenthesized expression --- .../src/parser/parser/type_expression.rs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 83e10505cb6..9c732a1d69c 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -161,23 +161,26 @@ impl<'a> Parser<'a> { } fn parse_parenthesized_type_expression(&mut self) -> Option { - if !self.eat_left_paren() { - return None; - } - - return match self.parse_type_expression() { - Ok(type_expr) => { - if !self.eat_right_paren() { - // TODO: error (expected `)`) + // Make sure not to parse `()` as a parenthesized expression + if self.token.token() == &Token::LeftParen && self.next_token.token() != &Token::RightParen + { + self.next_token(); + match self.parse_type_expression() { + Ok(type_expr) => { + if !self.eat_right_paren() { + // TODO: error (expected `)`) + } + Some(type_expr) + } + Err(error) => { + self.errors.push(error); + self.eat_right_paren(); + None } - Some(type_expr) - } - Err(error) => { - self.errors.push(error); - self.eat_right_paren(); - None } - }; + } else { + None + } } fn expected_type_expression_after_this( From 7cac16a547d8083b15eefec23e97351301071722 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 14:09:09 -0300 Subject: [PATCH 142/229] Will implement parse_type_or_type_expression next --- .../src/parser/parser/generics.rs | 21 ++----------------- .../src/parser/parser/type_expression.rs | 17 ++++++++++++++- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index c737cbb2bc5..d2788118243 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -3,7 +3,7 @@ use noirc_errors::Span; use crate::{ ast::{ GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, + UnresolvedType, UnresolvedTypeData, }, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, @@ -138,24 +138,7 @@ impl<'a> Parser<'a> { let typ = self.parse_type_or_error(); generic_type_args.named_args.push((ident, typ)); } else { - let typ = if let Ok(type_expr) = self.parse_type_expression() { - let span = type_expr.span(); - if let UnresolvedTypeExpression::Variable(path) = type_expr { - let generics = self.parse_generic_type_args(); - Some(UnresolvedType { - typ: UnresolvedTypeData::Named(path, generics, false), - span, - }) - } else { - Some(UnresolvedType { - typ: UnresolvedTypeData::Expression(type_expr), - span, - }) - } - } else { - self.parse_type() - }; - + let typ = self.parse_type_or_type_expression(); let Some(typ) = typ else { // TODO: error? (not sure if this is `<>` so test that) self.eat_greater(); diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 9c732a1d69c..a02ea068230 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,5 +1,8 @@ use crate::{ - ast::{Expression, ExpressionKind, Literal, UnresolvedTypeExpression}, + ast::{ + Expression, ExpressionKind, Literal, UnresolvedType, UnresolvedTypeData, + UnresolvedTypeExpression, + }, parser::{ParserError, ParserErrorReason}, token::Token, BinaryTypeOperator, @@ -10,6 +13,18 @@ use acvm::acir::AcirField; use super::Parser; impl<'a> Parser<'a> { + pub(crate) fn parse_type_or_type_expression(&mut self) -> Option { + if let Some(typ) = self.parse_type() { + return Some(typ); + } + + let start_span = self.current_token_span; + let type_expr = self.parse_type_expression().ok()?; + let typ = UnresolvedTypeData::Expression(type_expr); + let span = self.span_since(start_span); + Some(UnresolvedType { typ, span }) + } + pub(crate) fn parse_type_expression( &mut self, ) -> Result { From db40e9bba0d1050e4eb5621eaaed1de191ac710d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 15:22:53 -0300 Subject: [PATCH 143/229] Parse type or type expression --- .../src/parser/parser/type_expression.rs | 347 +++++++++++++++++- 1 file changed, 329 insertions(+), 18 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index a02ea068230..4be29291c38 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,6 +1,6 @@ use crate::{ ast::{ - Expression, ExpressionKind, Literal, UnresolvedType, UnresolvedTypeData, + Expression, ExpressionKind, GenericTypeArgs, Literal, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }, parser::{ParserError, ParserErrorReason}, @@ -9,22 +9,11 @@ use crate::{ }; use acvm::acir::AcirField; +use noirc_errors::Span; use super::Parser; impl<'a> Parser<'a> { - pub(crate) fn parse_type_or_type_expression(&mut self) -> Option { - if let Some(typ) = self.parse_type() { - return Some(typ); - } - - let start_span = self.current_token_span; - let type_expr = self.parse_type_expression().ok()?; - let typ = UnresolvedTypeData::Expression(type_expr); - let span = self.span_since(start_span); - Some(UnresolvedType { typ, span }) - } - pub(crate) fn parse_type_expression( &mut self, ) -> Result { @@ -36,8 +25,15 @@ impl<'a> Parser<'a> { fn parse_add_or_subtract_type_expression(&mut self) -> Option { let start_span = self.current_token_span; - let mut lhs = self.parse_multiply_or_divide_or_modulo_type_expression()?; + 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 @@ -63,15 +59,22 @@ impl<'a> Parser<'a> { } } - Some(lhs) + lhs } fn parse_multiply_or_divide_or_modulo_type_expression( &mut self, ) -> Option { let start_span = self.current_token_span; - let mut lhs = self.parse_term_type_expression()?; + 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 @@ -100,7 +103,7 @@ impl<'a> Parser<'a> { } } - Some(lhs) + lhs } fn parse_term_type_expression(&mut self) -> Option { @@ -198,6 +201,158 @@ impl<'a> Parser<'a> { } } + 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; + 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 !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 !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) { + 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_after_this_error(); + 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; + + let path = self.parse_path(); + if !path.is_empty() { + 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 { + // TODO: error + return None; + }; + + let typ_span = typ.span; + if let UnresolvedTypeData::Expression(type_expr) = typ.typ { + if !self.eat_right_paren() { + // TODO: error (expected `)`) + } + 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 mut types = vec![typ]; + loop { + if !self.eat_commas() { + // TODO: error (missing comma separating tuple types) + } + + let typ = self.parse_type_or_error(); + types.push(typ); + + if self.eat_right_paren() { + break; + } + } + + Some(UnresolvedType { + typ: UnresolvedTypeData::Tuple(types), + span: self.span_since(start_span), + }) + } + fn expected_type_expression_after_this( &mut self, ) -> Result { @@ -208,9 +363,42 @@ impl<'a> Parser<'a> { } } +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 crate::{ast::UnresolvedTypeExpression, parser::Parser, BinaryTypeOperator}; + use core::panic; + + use crate::{ + ast::{UnresolvedTypeData, UnresolvedTypeExpression}, + parser::Parser, + BinaryTypeOperator, + }; #[test] fn parses_constant_type_expression() { @@ -270,4 +458,127 @@ mod tests { assert!(parser.errors.is_empty()); assert_eq!(expr.to_string(), "(0 - N)"); } + + #[test] + fn parse_type_or_type_expression_constant() { + let src = "42"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeData::Unit = typ.typ else { + panic!("Expected unit type"); + }; + } + + #[test] + fn parses_type_or_type_expression_parenthesized_type() { + let src = "(Field)"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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 mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + 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_var_minus_one() { + let src = "N - 1"; + let mut parser = Parser::for_str(src); + let typ = parser.parse_type_or_type_expression().unwrap(); + assert!(parser.errors.is_empty()); + let UnresolvedTypeData::Expression(expr) = typ.typ else { + panic!("Expected expression type"); + }; + assert_eq!(expr.to_string(), "(N - 1)"); + } } From f74a1d2d3ee493dfde180e52a6e08f10a7522d21 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 15:38:10 -0300 Subject: [PATCH 144/229] Better way to parse path --- .../src/hir/comptime/interpreter/builtin.rs | 10 ++- .../src/parser/parser/expression.rs | 8 +-- .../noirc_frontend/src/parser/parser/path.rs | 63 ++++++++++++++----- .../src/parser/parser/pattern.rs | 5 +- .../src/parser/parser/type_expression.rs | 11 +--- .../noirc_frontend/src/parser/parser/types.rs | 8 +-- .../src/parser/parser/use_tree.rs | 5 ++ .../src/parser/parser/where_clause.rs | 2 +- 8 files changed, 71 insertions(+), 41 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index a6b1e5af6f4..583fcd56fb6 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -707,9 +707,13 @@ fn quoted_as_module( ) -> IResult { let argument = check_one_argument(arguments, location)?; - let path = - parse(interpreter.elaborator.interner, argument, Parser::parse_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| { diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 132accbc460..598aa212e70 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -293,10 +293,9 @@ impl<'a> Parser<'a> { } fn parse_path_expr(&mut self, allow_constructors: bool) -> Option { - let path = self.parse_path(); - if path.is_empty() { + let Some(path) = self.parse_path() else { return None; - } + }; if allow_constructors && self.eat_left_brace() { return Some(self.parse_constructor(path)); @@ -412,8 +411,7 @@ impl<'a> Parser<'a> { return None; } - let path = self.parse_path(); - if !path.is_empty() { + if let Some(path) = self.parse_path() { let expr = Expression { kind: ExpressionKind::Variable(path), span: self.span_since(start_span), diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 5cce9a7abde..58498f5c346 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -9,24 +9,51 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - pub(crate) fn parse_path(&mut self) -> Path { + #[cfg(test)] + pub(crate) fn parse_path_or_error(&mut self) -> Path { + if let Some(path) = self.parse_path() { + path + } else { + // TODO: error (expected path) + Path { + segments: Vec::new(), + kind: PathKind::Plain, + span: self.span_at_previous_token_end(), + } + } + } + + pub(crate) fn parse_path(&mut self) -> Option { self.parse_path_impl( true, // allow turbofish ) } - pub(crate) fn parse_path_no_turbofish(&mut self) -> Path { + pub(crate) fn parse_path_no_turbofish_or_error(&mut self) -> Path { + if let Some(path) = self.parse_path_no_turbofish() { + path + } else { + // TODO: error (expected path) + Path { + segments: Vec::new(), + kind: PathKind::Plain, + span: self.span_at_previous_token_end(), + } + } + } + + pub(crate) fn parse_path_no_turbofish(&mut self) -> Option { self.parse_path_impl( false, // allow turbofish ) } - pub(super) fn parse_path_impl(&mut self, allow_turbofish: bool) -> Path { + pub(super) fn parse_path_impl(&mut self, allow_turbofish: bool) -> Option { let start_span = self.current_token_span; let kind = self.parse_path_kind(); if kind != PathKind::Plain && !self.eat_double_colon() { - // TODO: error + // TODO: error (missing `::` after crate/dep/super) } self.parse_path_after_kind(kind, allow_turbofish, start_span) @@ -37,7 +64,7 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, start_span: Span, - ) -> Path { + ) -> Option { let mut segments = Vec::new(); if self.token.kind() == TokenKind::Ident { @@ -70,7 +97,11 @@ impl<'a> Parser<'a> { // TODO: error } - Path { segments, kind, span: self.span_since(start_span) } + if segments.is_empty() && kind == PathKind::Plain { + None + } else { + Some(Path { segments, kind, span: self.span_since(start_span) }) + } } pub(super) fn parse_path_generics( @@ -110,7 +141,7 @@ impl<'a> Parser<'a> { if !self.eat_keyword(Keyword::As) { // TODO: error (expected `as`) } - let trait_path = self.parse_path_no_turbofish(); + let trait_path = self.parse_path_no_turbofish_or_error(); let trait_generics = self.parse_generic_type_args(); if !self.eat_greater() { // TODO: error (expected `>`) @@ -138,7 +169,7 @@ mod tests { fn parses_plain_one_segment() { let src = "foo"; let mut parser = Parser::for_str(src); - let path = parser.parse_path(); + let path = parser.parse_path_or_error(); assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 1); @@ -150,7 +181,7 @@ mod tests { fn parses_plain_two_segments() { let src = "foo::bar"; let mut parser = Parser::for_str(src); - let path = parser.parse_path(); + let path = parser.parse_path_or_error(); assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 2); @@ -164,7 +195,7 @@ mod tests { fn parses_crate_two_segments() { let src = "crate::foo::bar"; let mut parser = Parser::for_str(src); - let path = parser.parse_path(); + let path = parser.parse_path_or_error(); dbg!(path.to_string()); assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Crate); @@ -179,7 +210,7 @@ mod tests { fn parses_super_two_segments() { let src = "super::foo::bar"; let mut parser = Parser::for_str(src); - let path = parser.parse_path(); + let path = parser.parse_path_or_error(); assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Super); assert_eq!(path.segments.len(), 2); @@ -193,7 +224,7 @@ mod tests { fn parses_dep_two_segments() { let src = "dep::foo::bar"; let mut parser = Parser::for_str(src); - let path = parser.parse_path(); + let path = parser.parse_path_or_error(); assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Dep); assert_eq!(path.segments.len(), 2); @@ -207,7 +238,7 @@ mod tests { fn parses_plain_one_segment_with_trailing_colons() { let src = "foo::"; let mut parser = Parser::for_str(src); - let path = parser.parse_path(); + let path = parser.parse_path_or_error(); assert_eq!(path.span.end() as usize, src.len() - 2); assert_eq!(parser.errors.len(), 0); // TODO: this should be 1 assert_eq!(path.kind, PathKind::Plain); @@ -220,7 +251,7 @@ mod tests { fn parses_with_turbofish() { let src = "foo::::bar"; let mut parser = Parser::for_str(src); - let mut path = parser.parse_path(); + let mut path = parser.parse_path_or_error(); assert!(parser.errors.is_empty()); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 2); @@ -237,7 +268,7 @@ mod tests { fn parses_path_stops_before_trailing_double_colon() { let src = "foo::bar::"; let mut parser = Parser::for_str(src); - let path = parser.parse_path(); + let path = parser.parse_path_or_error(); assert_eq!(path.span.end() as usize, src.len() - 2); assert!(parser.errors.is_empty()); assert_eq!(path.to_string(), "foo::bar"); @@ -247,7 +278,7 @@ mod tests { 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(); + let path = parser.parse_path_or_error(); assert_eq!(path.span.end() as usize, src.len() - 2); assert!(parser.errors.is_empty()); assert_eq!(path.to_string(), "foo::bar::<1>"); diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 6fd48c35bf0..cdbca64b340 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -66,13 +66,12 @@ impl<'a> Parser<'a> { return pattern; } - let mut path = self.parse_path(); - if path.is_empty() { + let Some(mut path) = self.parse_path() else { self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); // TODO: error return Pattern::Identifier(Ident::default()); - } + }; if self.eat_left_brace() { return self.parse_struct_pattern(path); diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 4be29291c38..600f868dff5 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -170,12 +170,8 @@ impl<'a> Parser<'a> { } fn parse_variable_type_expression(&mut self) -> Option { - let path = self.parse_path(); - if path.is_empty() { - None - } else { - Some(UnresolvedTypeExpression::Variable(path)) - } + let path = self.parse_path()?; + Some(UnresolvedTypeExpression::Variable(path)) } fn parse_parenthesized_type_expression(&mut self) -> Option { @@ -275,8 +271,7 @@ impl<'a> Parser<'a> { fn parse_atom_type_or_type_expression(&mut self) -> Option { let start_span = self.current_token_span; - let path = self.parse_path(); - if !path.is_empty() { + 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); diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index a6090d8bf0a..22c1693bd92 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -53,8 +53,7 @@ impl<'a> Parser<'a> { return Some(typ); } - let path = self.parse_path_no_turbofish(); - if !path.is_empty() { + if let Some(path) = self.parse_path_no_turbofish() { let generics = self.parse_generic_type_args(); return Some(UnresolvedTypeData::Named(path, generics, false)); } @@ -309,11 +308,10 @@ impl<'a> Parser<'a> { return None; } - let path = self.parse_path_no_turbofish(); - if path.is_empty() { + let Some(path) = self.parse_path_no_turbofish() else { // TODO: error (expected path after impl) return None; - } + }; let generics = self.parse_generic_type_args(); diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 1186bd4be09..765ba40bae6 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -32,6 +32,11 @@ impl<'a> Parser<'a> { kind, false, // allow turbofish start_span, ); + let prefix = prefix.unwrap_or_else(|| Path { + segments: Vec::new(), + kind: PathKind::Plain, + span: self.span_since(start_span), + }); let trailing_double_colon = if prefix.segments.is_empty() && kind != PathKind::Plain { true diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 3b3957a0ce3..1bbb81737b1 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -68,7 +68,7 @@ impl<'a> Parser<'a> { } pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { - let trait_path = self.parse_path_no_turbofish(); + let trait_path = self.parse_path_no_turbofish_or_error(); let trait_generics = self.parse_generic_type_args(); TraitBound { trait_path, trait_generics, trait_id: None } } From 8ac98cc54bed92f8d6a4aaade076cc46e0ea928f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 29 Sep 2024 16:10:52 -0300 Subject: [PATCH 145/229] LValue for parentheses --- compiler/noirc_frontend/src/ast/statement.rs | 1 + .../noirc_frontend/src/parser/parser/statement.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 05fde1da5aa..d426563cec0 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -746,6 +746,7 @@ impl LValue { None } } + ExpressionKind::Parenthesized(expr) => LValue::from_expression(*expr), ExpressionKind::Interned(id) => Some(LValue::Interned(id, span)), _ => None, } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index e2e0642f31c..ecb8dc3561f 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -504,6 +504,17 @@ mod tests { assert_eq!(assign.expression.to_string(), "1"); } + #[test] + fn parses_assignment_with_parentheses() { + let src = "(x)[0] = 1"; + let mut parser = Parser::for_str(&src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + let StatementKind::Assign(..) = statement.kind else { + panic!("Expected assign"); + }; + } + #[test] fn parses_op_assignment() { let src = "x += 1"; From a3b68ff2b07b9783c1ef1d0fc59db0fc3e62cae4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 08:17:17 -0300 Subject: [PATCH 146/229] Some error handling --- .../src/hir/comptime/interpreter/builtin.rs | 2 +- .../noirc_frontend/src/hir/comptime/value.rs | 2 +- compiler/noirc_frontend/src/parser/errors.rs | 31 ++++-------- compiler/noirc_frontend/src/parser/parser.rs | 49 +++++++++++++++++-- .../noirc_frontend/src/parser/parser/call.rs | 4 +- .../src/parser/parser/expression.rs | 46 ++++++----------- .../src/parser/parser/function.rs | 4 +- .../src/parser/parser/generics.rs | 6 +-- .../noirc_frontend/src/parser/parser/impls.rs | 43 +++++++++------- .../noirc_frontend/src/parser/parser/item.rs | 46 +++++++++++++---- .../src/parser/parser/lambda.rs | 3 +- .../src/parser/parser/module.rs | 45 ++++++++++------- .../noirc_frontend/src/parser/parser/path.rs | 20 +++----- .../src/parser/parser/pattern.rs | 25 +++++++--- .../src/parser/parser/statement.rs | 18 ++++--- .../src/parser/parser/structs.rs | 25 +++++----- .../src/parser/parser/traits.rs | 12 ++--- .../src/parser/parser/type_alias.rs | 10 ++-- .../src/parser/parser/type_expression.rs | 4 +- .../noirc_frontend/src/parser/parser/types.rs | 32 ++++++------ .../src/parser/parser/use_tree.rs | 28 ++++++----- .../src/parser/parser/where_clause.rs | 19 +++---- 22 files changed, 269 insertions(+), 205 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 583fcd56fb6..68455879028 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -2441,7 +2441,7 @@ fn module_add_item( let module_id = get_module(self_argument)?; let module_data = interpreter.elaborator.get_module(module_id); - let parser = Parser::parse_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/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index dccabd2f874..a4034dbcc2c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -524,7 +524,7 @@ impl Value { location: Location, interner: &NodeInterner, ) -> IResult> { - let parser = Parser::parse_items; + let parser = Parser::parse_top_level_items; match self { Value::Quoted(tokens) => { parse_tokens(tokens, interner, parser, location, "top-level item") diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index c8ce51e2118..8db72b4926e 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -19,22 +19,15 @@ pub enum ParserErrorReason { UnexpectedSemicolon, #[error("Unexpected `,`")] UnexpectedComma, - #[error("Expected a `,` separating these two expressions")] - MissingCommaSeparatingExpressions, - #[error("Expected a `,` separating these two parameters")] - MissingCommaSeparatingParameters, - #[error("Expected a `,` separating these two generic parameters")] - MissingCommaSeparatingGenerics, - #[error("Expected a `,` separating these two trait bounds")] - MissingCommaSeparatingTraitBounds, - #[error("Expected a `+` separating these two trait bounds")] - MissingPlusSeparatingTraitBounds, - #[error("Expected a `,` separating these two arguments")] - MissingCommaSeparatingArguments, - #[error("Expected a `,` separating these two constructor fields")] - MissingCommaSeparatingConstructorFields, - #[error("Expected an identifier after `fn`")] - ExpectedIdentifierAfterFn, + #[error("Expected a `{token}` separating these two {items}")] + ExpectedTokenSeparatingTwoItems { token: String, items: String }, + #[error("Expected an identifier, found {found}")] + ExpectedIdentifier { found: Token }, + #[error("Expected a item, found {found}")] + ExpectedItem { found: Token }, + #[error("Invalid left-hand side of assignment")] + InvalidLeftHandSideOfAssignment, + #[error("Missing type for function parameter")] MissingTypeForFunctionParameter, #[error("Missing type for numeric generic")] @@ -65,8 +58,6 @@ pub enum ParserErrorReason { ExpectedBracketAfterSlice, #[error("Expected `>` after str type length")] ExpectedGreaterAfterStringTypeLength, - #[error("Expected str type length after `str`")] - ExpectedStringTypeLength, #[error("Expected `{{` or `if` after `else`")] ExpectedLeftBraceOfIfAfterElse, #[error("Expected a type after this")] @@ -82,10 +73,6 @@ pub enum ParserErrorReason { ExpectedFieldName(Token), #[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")] diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 3e409e3cf2a..9f963ee31fa 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -44,7 +44,7 @@ mod where_clause; pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { let lexer = Lexer::new(source_program); let mut parser = Parser::for_lexer(lexer); - let program = parser.parse_module(); + let program = parser.parse_program(); let errors = parser.errors; (program, errors) } @@ -115,9 +115,15 @@ impl<'a> Parser<'a> { parser } - pub(crate) fn parse_module(&mut self) -> ParsedModule { + pub(crate) fn parse_program(&mut self) -> ParsedModule { + self.parse_module( + false, // nested + ) + } + + pub(crate) fn parse_module(&mut self, nested: bool) -> ParsedModule { let inner_doc_comments = self.parse_inner_doc_comments(); - let items = self.parse_items(); + let items = self.parse_items(nested); ParsedModule { items, inner_doc_comments } } @@ -393,6 +399,18 @@ impl<'a> Parser<'a> { } } + fn eat_keyword_or_error(&mut self, keyword: Keyword) { + if !self.eat_keyword(keyword) { + self.expected_token(Token::Keyword(keyword)) + } + } + + fn eat_or_error(&mut self, token: Token) { + if !self.eat(token.clone()) { + self.expected_token(token) + } + } + fn is_eof(&self) -> bool { self.token.token() == &Token::EOF } @@ -410,6 +428,31 @@ impl<'a> Parser<'a> { Span::from(self.previous_token_span.end()..self.previous_token_span.end()) } + fn expected_identifier(&mut self) { + self.push_error( + ParserErrorReason::ExpectedIdentifier { found: self.token.token().clone() }, + self.current_token_span, + ); + } + + fn expected_token(&mut self, token: Token) { + self.errors.push(ParserError::expected_token( + token, + self.token.token().clone(), + self.current_token_span, + )) + } + + fn expected_token_separating_items(&mut self, token: &str, items: &str, span: Span) { + self.push_error( + ParserErrorReason::ExpectedTokenSeparatingTwoItems { + token: token.to_string(), + items: items.to_string(), + }, + span, + ); + } + fn push_error(&mut self, reason: ParserErrorReason, span: Span) { self.errors.push(ParserError::with_reason(reason, span)); } diff --git a/compiler/noirc_frontend/src/parser/parser/call.rs b/compiler/noirc_frontend/src/parser/parser/call.rs index a400b90ee6e..f83ffe22d4a 100644 --- a/compiler/noirc_frontend/src/parser/parser/call.rs +++ b/compiler/noirc_frontend/src/parser/parser/call.rs @@ -1,4 +1,4 @@ -use crate::{ast::Expression, parser::ParserErrorReason}; +use crate::ast::Expression; use super::Parser; @@ -22,7 +22,7 @@ impl<'a> Parser<'a> { }; if !trailing_comma && !arguments.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingArguments, start_span); + self.expected_token_separating_items(",", "arguments", start_span); } arguments.push(expr); diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 598aa212e70..7b7ea81f01f 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -110,10 +110,7 @@ impl<'a> Parser<'a> { } else if let Some(int) = self.eat_int() { Ident::new(int.to_string(), self.previous_token_span) } else { - self.push_error( - ParserErrorReason::ExpectedIdentifierAfterDot, - self.current_token_span, - ); + self.expected_identifier(); continue; }; @@ -168,9 +165,7 @@ impl<'a> Parser<'a> { if self.eat_left_bracket() { let index = self.parse_expression_or_error(); - if !self.eat_right_bracket() { - // TODO: error - } + self.eat_or_error(Token::RightBracket); let kind = ExpressionKind::Index(Box::new(IndexExpression { collection: atom, index })); let span = self.span_since(start_span); @@ -311,17 +306,12 @@ impl<'a> Parser<'a> { loop { let start_span = self.current_token_span; let Some(ident) = self.eat_ident() else { - if !self.eat_right_brace() { - // TODO: error - } + self.eat_or_error(Token::RightBrace); break; }; if !trailing_comma && !fields.is_empty() { - self.push_error( - ParserErrorReason::MissingCommaSeparatingConstructorFields, - start_span, - ); + self.expected_token_separating_items(",", "constructor fields", start_span); } if self.eat_colon() { @@ -397,7 +387,7 @@ impl<'a> Parser<'a> { } let Some(block) = self.parse_block_expression() else { - // TODO: error (expected `{` after comptime) + self.expected_token(Token::LeftBrace); return None; }; @@ -422,9 +412,7 @@ impl<'a> Parser<'a> { let span_at_left_paren = self.current_token_span; if self.eat_left_paren() { let expr = self.parse_expression_or_error(); - if !self.eat_right_paren() { - // TODO: error (missing `)` after quoted expression) - } + self.eat_or_error(Token::RightParen); let expr = Expression { kind: ExpressionKind::Parenthesized(Box::new(expr)), span: self.span_since(span_at_left_paren), @@ -444,17 +432,12 @@ impl<'a> Parser<'a> { }; let typ = UnresolvedType { typ, span: self.span_since(start_span) }; - if !self.eat_double_colon() { - // TODO: error (expected `::` after type) - } + self.eat_or_error(Token::DoubleColon); let item = if let Some(ident) = self.eat_ident() { ident } else { - self.push_error( - ParserErrorReason::ExpectedIdentifierAfterColons, - self.current_token_span, - ); + self.expected_identifier(); Ident::new(String::new(), self.span_at_previous_token_end()) }; @@ -571,7 +554,7 @@ impl<'a> Parser<'a> { }; if !trailing_comma { - self.push_error(ParserErrorReason::MissingCommaSeparatingExpressions, start_span); + self.expected_token_separating_items(",", "expressions", start_span); } exprs.push(expr); @@ -604,7 +587,7 @@ impl<'a> Parser<'a> { break; }; if !trailing_comma && !exprs.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingExpressions, start_span); + self.expected_token_separating_items(",", "expressions", start_span); } exprs.push(expr); @@ -651,9 +634,6 @@ impl<'a> Parser<'a> { }; statements.push((statement, (token, span))); - - // TODO: error if missing semicolon and statement requires one and is not the last one in the block - self.eat_semicolons(); } let statements = self.check_statements_require_semicolon(statements); @@ -936,7 +916,11 @@ mod tests { let mut parser = Parser::for_str(&src); parser.parse_expression(); let reason = get_single_error(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::MissingCommaSeparatingExpressions)); + let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { + panic!("Expected a different error"); + }; + assert_eq!(token, ","); + assert_eq!(items, "expressions"); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index a1a43fba229..8fdf90920b1 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -80,7 +80,7 @@ impl<'a> Parser<'a> { allow_self: bool, ) -> FunctionDefinitionWithOptionalBody { let Some(name) = self.eat_ident() else { - self.push_error(ParserErrorReason::ExpectedIdentifierAfterFn, self.current_token_span); + self.expected_identifier(); return empty_function(self.previous_token_span); }; @@ -153,7 +153,7 @@ impl<'a> Parser<'a> { } if !trailing_comma && !parameters.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); + self.expected_token_separating_items(",", "parameters", start_span); } match pattern_or_self { diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index d2788118243..342d82a9c76 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -33,7 +33,7 @@ impl<'a> Parser<'a> { }; if !trailing_comma && !generics.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); + self.expected_token_separating_items(",", "generic parameters", start_span); } generics.push(generic); @@ -130,7 +130,7 @@ impl<'a> Parser<'a> { let ident = self.eat_ident().unwrap(); if !trailing_comma && !generic_type_args.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); + self.expected_token_separating_items(",", "generic parameters", start_span); } self.eat_assign(); @@ -146,7 +146,7 @@ impl<'a> Parser<'a> { }; if !trailing_comma && !generic_type_args.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingGenerics, start_span); + self.expected_token_separating_items(",", "generic parameters", start_span); } generic_type_args.ordered_args.push(typ); diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 64288872d10..b6bc81b6bc9 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -7,7 +7,7 @@ use crate::{ UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, parser::ParserErrorReason, - token::Keyword, + token::{Keyword, Token}, }; use super::Parser; @@ -48,12 +48,20 @@ impl<'a> Parser<'a> { let mut methods = Vec::new(); if !self.eat_left_brace() { - // TODO: error + self.expected_token(Token::LeftBrace); return methods; } loop { - // TODO: maybe require visibility to always come first + if self.eat_right_brace() { + break; + } + + if self.is_eof() { + // TODO: error + break; + } + let doc_comments = self.parse_outer_doc_comments(); let start_span = self.current_token_span; let attributes = self.parse_attributes(); @@ -68,18 +76,14 @@ impl<'a> Parser<'a> { true, // allow_self ); methods.push((Documented::new(method, doc_comments), self.span_since(start_span))); + continue; + } - if self.eat_right_brace() { - break; - } - } else { - // TODO: error if visibility, unconstrained or comptime were found - - if !self.eat_right_brace() { - // TODO: error - } + // TODO: error if visibility, unconstrained or comptime were found - break; + if self.token.token() != &Token::RightBrace { + // TODO: error + self.next_token(); } } @@ -110,7 +114,7 @@ impl<'a> Parser<'a> { let mut items = Vec::new(); if !self.eat_left_brace() { - // TODO: error + self.expected_token(Token::LeftBrace); return items; } @@ -187,7 +191,7 @@ impl<'a> Parser<'a> { } let Some(name) = self.eat_ident() else { - // TODO: error + self.expected_identifier(); self.eat_semicolons(); return Some(TraitImplItemKind::Type { name: Ident::default(), @@ -224,7 +228,7 @@ impl<'a> Parser<'a> { let expr = if self.eat_assign() { self.parse_expression_or_error() } else { - // TODO: error + self.expected_token(Token::Assign); Expression { kind: ExpressionKind::Error, span: Span::default() } }; @@ -425,7 +429,7 @@ mod tests { fn parse_empty_impl_missing_right_brace() { let src = "impl Foo {"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); // TODO: there should be an error here + assert_eq!(errors.len(), 0); // TODO: this should be 1 assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Impl(type_impl) = &item.kind else { @@ -436,15 +440,16 @@ mod tests { #[test] fn parse_empty_impl_incorrect_body() { - let src = "impl Foo { hello"; + let src = "impl Foo { hello fn foo() {} }"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); // TODO: there should be errors here + assert_eq!(errors.len(), 0); // TODO: this should be 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] diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 21decf54c02..e476784b8a6 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -1,16 +1,48 @@ use crate::{ - parser::{Item, ItemKind}, - token::Keyword, + parser::{Item, ItemKind, ParserErrorReason}, + token::{Keyword, Token}, }; use super::{impls::Impl, Parser}; impl<'a> Parser<'a> { - pub(crate) fn parse_items(&mut self) -> Vec { + pub(crate) fn parse_top_level_items(&mut self) -> Vec { + self.parse_items( + false, // nested + ) + } + + pub(crate) fn parse_items(&mut self, nested: bool) -> Vec { let mut items = Vec::new(); - while let Some(item) = self.parse_item() { - items.push(item); + loop { + if nested && self.token.token() == &Token::RightBrace { + break; + } + + if self.is_eof() { + break; + } + + if let Some(item) = self.parse_item() { + items.push(item); + continue; + } + + let is_error_token = match self.token.token() { + Token::RightBrace => !nested, + Token::EOF => false, + _ => true, + }; + + if is_error_token { + self.push_error( + ParserErrorReason::ExpectedItem { found: self.token.token().clone() }, + self.current_token_span, + ); + // We'll try parsing an item on the next token + self.next_token(); + } } items @@ -97,10 +129,6 @@ impl<'a> Parser<'a> { ))); } - // TODO: error - // We'll try parsing an item on the next token - self.next_token(); - None } diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index 4743d7ae252..45de6e94b2b 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -1,6 +1,5 @@ use crate::{ ast::{ExpressionKind, Lambda, Pattern, UnresolvedType}, - parser::ParserErrorReason, token::Token, }; @@ -45,7 +44,7 @@ impl<'a> Parser<'a> { } if !trailing_comma && !parameters.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); + self.expected_token_separating_items(",", "parameters", start_span); } let typ = self.parse_optional_type_annotation(); diff --git a/compiler/noirc_frontend/src/parser/parser/module.rs b/compiler/noirc_frontend/src/parser/parser/module.rs index 5196cd4b784..ae384b60d2e 100644 --- a/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -3,7 +3,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, ModuleDeclaration}, parser::{ItemKind, ParsedSubModule}, - token::Attribute, + token::{Attribute, Token}, }; use super::Parser; @@ -16,25 +16,30 @@ impl<'a> Parser<'a> { ) -> ItemKind { let outer_attributes = self.validate_secondary_attributes(attributes); - if let Some(ident) = self.eat_ident() { - if self.eat_left_brace() { - let contents = self.parse_module(); - if !self.eat_right_brace() { - // TODO: error - } - ItemKind::Submodules(ParsedSubModule { - name: ident, - contents, - outer_attributes, - is_contract, - }) - } else { - self.eat_semicolons(); - ItemKind::ModuleDecl(ModuleDeclaration { ident, outer_attributes }) - } + let Some(ident) = self.eat_ident() else { + self.expected_identifier(); + return ItemKind::ModuleDecl(ModuleDeclaration { + 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 { + name: ident, + contents, + outer_attributes, + is_contract, + }) } else { - // TODO: error - ItemKind::ModuleDecl(ModuleDeclaration { ident: Ident::default(), outer_attributes }) + if !self.eat_semicolons() { + self.expected_token(Token::Semicolon); + } + ItemKind::ModuleDecl(ModuleDeclaration { ident, outer_attributes }) } } } @@ -61,6 +66,7 @@ mod tests { fn parse_submodule() { let src = "mod foo { mod bar; }"; let (module, errors) = parse_program(src); + dbg!(&errors); assert!(errors.is_empty()); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -76,6 +82,7 @@ mod tests { fn parse_contract() { let src = "contract foo {}"; let (module, errors) = parse_program(src); + dbg!(&errors); assert!(errors.is_empty()); assert_eq!(module.items.len(), 1); let item = &module.items[0]; diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 58498f5c346..7ba9f3f4b90 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -52,8 +52,8 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let kind = self.parse_path_kind(); - if kind != PathKind::Plain && !self.eat_double_colon() { - // TODO: error (missing `::` after crate/dep/super) + if kind != PathKind::Plain { + self.eat_or_error(Token::DoubleColon); } self.parse_path_after_kind(kind, allow_turbofish, start_span) @@ -93,8 +93,6 @@ impl<'a> Parser<'a> { break; } } - } else { - // TODO: error } if segments.is_empty() && kind == PathKind::Plain { @@ -138,21 +136,15 @@ impl<'a> Parser<'a> { } let typ = self.parse_type_or_error(); - if !self.eat_keyword(Keyword::As) { - // TODO: error (expected `as`) - } + 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(); - if !self.eat_greater() { - // TODO: error (expected `>`) - } - if !self.eat_double_colon() { - // TODO: error (expected `::`) - } + self.eat_or_error(Token::Greater); + self.eat_or_error(Token::DoubleColon); let impl_item = if let Some(ident) = self.eat_ident() { ident } else { - // TODO: error (expected identifier) + self.expected_identifier(); Ident::new(String::new(), self.span_at_previous_token_end()) }; diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index cdbca64b340..be27fec4fb1 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -68,8 +68,6 @@ impl<'a> Parser<'a> { let Some(mut path) = self.parse_path() else { self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); - - // TODO: error return Pattern::Identifier(Ident::default()); }; @@ -95,6 +93,7 @@ impl<'a> Parser<'a> { } let mut patterns = Vec::new(); + let mut trailing_comma = false; loop { if self.eat_right_paren() { break; @@ -108,10 +107,13 @@ impl<'a> Parser<'a> { break; } + if !trailing_comma && !patterns.is_empty() { + self.expected_token_separating_items(",", "tuple elements", start_span); + } + patterns.push(pattern); - self.eat_commas(); - // TODO: error if no commas between patterns + trailing_comma = self.eat_commas(); } Some(Pattern::Tuple(patterns, self.span_since(start_span))) @@ -121,25 +123,32 @@ impl<'a> Parser<'a> { let start_span = path.span(); let mut patterns = Vec::new(); + let mut trailing_comma = false; loop { if self.eat_right_brace() { break; } + let start_span = self.current_token_span; + let Some(ident) = self.eat_ident() else { - // TODO: error + self.expected_identifier(); + self.eat_right_brace(); break; }; + if !trailing_comma && !patterns.is_empty() { + self.expected_token_separating_items(",", "struct fields", start_span); + } + if self.eat_colon() { patterns.push((ident, self.parse_pattern())); } else { patterns.push((ident.clone(), Pattern::Identifier(ident))); } - self.eat_commas(); - // TODO: error if no comma between patterns + trailing_comma = self.eat_commas(); } Pattern::Struct(path, patterns, self.span_since(start_span)) @@ -251,7 +260,7 @@ mod tests { let src = "foo::Bar { x"; let mut parser = Parser::for_str(src); let typ = parser.parse_pattern(); - assert!(parser.errors.is_empty()); + assert_eq!(parser.errors.len(), 1); let Pattern::Struct(path, _, _) = typ else { panic!("Expected a struct pattern") }; assert_eq!(path.to_string(), "foo::Bar"); } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index ecb8dc3561f..7171591b3cd 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -101,7 +101,10 @@ impl<'a> Parser<'a> { let expression = self.parse_expression_or_error(); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); } else { - // TODO: error (invalid l-value) + self.push_error( + ParserErrorReason::InvalidLeftHandSideOfAssignment, + expression.span, + ); } } @@ -120,7 +123,10 @@ impl<'a> Parser<'a> { ); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); } else { - // TODO: error (invalid l-value) + self.push_error( + ParserErrorReason::InvalidLeftHandSideOfAssignment, + expression.span, + ); } } @@ -167,13 +173,13 @@ impl<'a> Parser<'a> { } let Some(identifier) = self.eat_ident() else { - // TODO: error (expected for identifier) + self.expected_identifier(); let identifier = Ident::default(); return Some(self.empty_for_loop(identifier, start_span)); }; if !self.eat_keyword(Keyword::In) { - // TODO: error (expected `in` after for identifier) + self.expected_token(Token::Keyword(Keyword::In)); return Some(self.empty_for_loop(identifier, start_span)); } @@ -192,7 +198,7 @@ impl<'a> Parser<'a> { span: self.span_since(block_start_span), } } else { - // TODO: error (expected for body) + self.expected_token(Token::LeftBrace); Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } }; @@ -262,7 +268,7 @@ impl<'a> Parser<'a> { let expression = if self.eat_assign() { self.parse_expression_or_error() } else { - // TODO: error + self.expected_token(Token::Assign); Expression { kind: ExpressionKind::Error, span: self.current_token_span } }; diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 8a4889167dc..f9d2e515564 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Documented, Ident, ItemVisibility, NoirStruct, StructField, UnresolvedGenerics}, - token::{Attribute, SecondaryAttribute}, + token::{Attribute, SecondaryAttribute, Token}, }; use super::Parser; @@ -17,7 +17,7 @@ impl<'a> Parser<'a> { let attributes = self.validate_secondary_attributes(attributes); let Some(name) = self.eat_ident() else { - // TODO: error + self.expected_identifier(); return self.empty_struct( Ident::default(), attributes, @@ -34,35 +34,36 @@ impl<'a> Parser<'a> { } if !self.eat_left_brace() { - // TODO: error + self.expected_token(Token::LeftBrace); return self.empty_struct(name, attributes, visibility, generics, start_span); } let mut fields = Vec::new(); + let mut trailing_comma = false; loop { let doc_comments = self.parse_outer_doc_comments(); + let start_span = self.current_token_span; let Some(name) = self.eat_ident() else { // TODO: error if there are doc comments break; }; - if !self.eat_colon() { - // TODO: error - } + self.eat_or_error(Token::Colon); let typ = self.parse_type_or_error(); + if !trailing_comma && !fields.is_empty() { + self.expected_token_separating_items(",", "struct fields", start_span); + } + fields.push(Documented::new(StructField { name, typ }, doc_comments)); - self.eat_commas(); - // TODO: error if no comma between fields + trailing_comma = self.eat_commas(); } - if !self.eat_right_brace() { - // TODO: error - } + self.eat_or_error(Token::RightBrace); NoirStruct { name, @@ -210,7 +211,7 @@ mod tests { fn parse_unclosed_struct() { let src = "struct Foo {"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); // TODO: there should be an error here + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Struct(noir_struct) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 09fb3576768..28dfa232fa9 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -5,7 +5,7 @@ use crate::{ Documented, Ident, ItemVisibility, NoirTrait, Pattern, TraitItem, UnresolvedType, UnresolvedTypeData, }, - token::{Attribute, Keyword, SecondaryAttribute}, + token::{Attribute, Keyword, SecondaryAttribute, Token}, }; use super::Parser; @@ -20,7 +20,7 @@ impl<'a> Parser<'a> { let attributes = self.validate_secondary_attributes(attributes); let Some(name) = self.eat_ident() else { - // TODO: error + self.expected_identifier(); return empty_trait(attributes, visibility, self.span_since(start_span)); }; @@ -43,7 +43,7 @@ impl<'a> Parser<'a> { let mut items = Vec::new(); if !self.eat_left_brace() { - // TODO: error + self.expected_token(Token::LeftBrace); return items; } @@ -94,7 +94,7 @@ impl<'a> Parser<'a> { let name = match self.eat_ident() { Some(name) => name, None => { - // TODO: error + self.expected_identifier(); Ident::default() } }; @@ -112,7 +112,7 @@ impl<'a> Parser<'a> { let name = match self.eat_ident() { Some(name) => name, None => { - // TODO: error + self.expected_identifier(); Ident::default() } }; @@ -120,7 +120,7 @@ impl<'a> Parser<'a> { let typ = if self.eat_colon() { self.parse_type_or_error() } else { - // TODO: error + self.expected_token(Token::Colon); UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } }; diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 44380b5a6d2..38e5eb3c088 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -1,13 +1,16 @@ use noirc_errors::Span; -use crate::ast::{Ident, NoirTypeAlias, UnresolvedType, UnresolvedTypeData}; +use crate::{ + ast::{Ident, NoirTypeAlias, UnresolvedType, UnresolvedTypeData}, + token::Token, +}; use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_type_alias(&mut self, start_span: Span) -> NoirTypeAlias { let Some(name) = self.eat_ident() else { - // TODO: error + self.expected_identifier(); return NoirTypeAlias { name: Ident::default(), generics: Vec::new(), @@ -19,10 +22,11 @@ impl<'a> Parser<'a> { let generics = self.parse_generics(); if !self.eat_assign() { + self.expected_token(Token::Assign); + let span = self.span_since(start_span); self.eat_semicolons(); - // TODO: error return NoirTypeAlias { name, generics, diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 600f868dff5..2b13c1cea1b 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -181,9 +181,7 @@ impl<'a> Parser<'a> { self.next_token(); match self.parse_type_expression() { Ok(type_expr) => { - if !self.eat_right_paren() { - // TODO: error (expected `)`) - } + self.eat_or_error(Token::RightParen); Some(type_expr) } Err(error) => { diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 22c1693bd92..5be82fa2896 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -133,7 +133,7 @@ impl<'a> Parser<'a> { } if !self.eat_less() { - self.push_error(ParserErrorReason::ExpectedStringTypeLength, self.current_token_span); + self.expected_token(Token::Less); let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); return Some(UnresolvedTypeData::String(expr)); } @@ -162,7 +162,7 @@ impl<'a> Parser<'a> { } if !self.eat_less() { - self.push_error(ParserErrorReason::ExpectedStringTypeLength, self.current_token_span); + 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))); @@ -177,14 +177,12 @@ impl<'a> Parser<'a> { }; if !self.eat_commas() { - // TODO: error (expected comma after fmtstr type expression) + self.expected_token(Token::Comma); } let typ = self.parse_type_or_error(); - if !self.eat_greater() { - // TODO: error (expected closing `>`) - } + self.eat_or_error(Token::Greater); Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))) } @@ -251,16 +249,15 @@ impl<'a> Parser<'a> { let env = if self.eat_left_bracket() { let typ = self.parse_type_or_error(); - if !self.eat_right_bracket() { - // TODO: error (expected `[` after `fn` env) - } + self.eat_or_error(Token::RightBracket); typ } else { UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) }; if !self.eat_left_paren() { - // TODO: error (expected `(` after `fn`) + self.expected_token(Token::LeftParen); + return Some(UnresolvedTypeData::Function( Vec::new(), Box::new(self.unspecified_type_at_previous_token_end()), @@ -285,7 +282,7 @@ impl<'a> Parser<'a> { } if !trailing_comma && !args.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingParameters, start_span); + self.expected_token_separating_items(",", "parameters", start_span); } args.push(typ); @@ -296,7 +293,7 @@ impl<'a> Parser<'a> { let ret = if self.eat(Token::Arrow) { self.parse_type_or_error() } else { - // TODO: error (expected `->` after function type arguments) + self.expected_token(Token::Arrow); UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) }; @@ -351,9 +348,7 @@ impl<'a> Parser<'a> { fn parses_mutable_reference_type(&mut self) -> Option { if self.eat(Token::Ampersand) { - if !self.eat_keyword(Keyword::Mut) { - // TODO: error - } + self.eat_keyword_or_error(Keyword::Mut); return Some(UnresolvedTypeData::MutableReference(Box::new( self.parse_type_or_error(), ))); @@ -407,16 +402,21 @@ impl<'a> Parser<'a> { let mut types = Vec::new(); let mut trailing_comma = false; loop { + let start_span = self.current_token_span; + let Some(typ) = self.parse_type() else { // TODO: error self.eat_right_paren(); break; }; + if !trailing_comma && !types.is_empty() { + self.expected_token_separating_items(",", "tuple elements", start_span); + } + types.push(typ); trailing_comma = self.eat_commas(); - // TODO: error if no comma between types if self.eat_right_paren() { break; diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 765ba40bae6..e30e1988dcc 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, Path, PathKind, UseTree, UseTreeKind}, - token::Keyword, + token::{Keyword, Token}, }; use super::Parser; @@ -12,13 +12,13 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let kind = self.parse_path_kind(); - if kind != PathKind::Plain && !self.eat_double_colon() { - // TODO: error + if kind != PathKind::Plain { + self.eat_or_error(Token::DoubleColon); } let use_tree = self.parse_use_tree_without_kind(start_span, kind); if !self.eat_semicolons() { - // TODO: error + self.expected_token(Token::Semicolon); } use_tree } @@ -47,21 +47,25 @@ impl<'a> Parser<'a> { if trailing_double_colon { if self.eat_left_brace() { let mut use_trees = Vec::new(); + let mut trailing_comma = false; loop { - let current_span = self.current_token_span; + let start_span = self.current_token_span; let use_tree = self.parse_use_tree_without_kind(self.current_token_span, PathKind::Plain); // If we didn't advance at all, we are done - if current_span == self.current_token_span { + if start_span == self.current_token_span { break; } + if !trailing_comma && !use_trees.is_empty() { + self.expected_token_separating_items(",", "use trees", start_span); + } + use_trees.push(use_tree); - self.eat_commas(); - // TODO: error if no comma between use trees + trailing_comma = self.eat_commas(); if self.eat_right_brace() { break; @@ -69,7 +73,7 @@ impl<'a> Parser<'a> { } UseTree { prefix, kind: UseTreeKind::List(use_trees) } } else { - // TODO: error + self.expected_token(Token::LeftBrace); self.parse_path_use_tree_end(prefix) } } else { @@ -172,7 +176,7 @@ mod tests { #[test] fn parse_with_crate_prefix() { - let src = "use crate::foo"; + let src = "use crate::foo;"; let (mut module, errors) = parse_program(src); assert!(errors.is_empty()); assert_eq!(module.items.len(), 1); @@ -192,7 +196,7 @@ mod tests { #[test] fn parse_with_dep_prefix() { - let src = "use dep::foo"; + let src = "use dep::foo;"; let (mut module, errors) = parse_program(src); assert!(errors.is_empty()); assert_eq!(module.items.len(), 1); @@ -212,7 +216,7 @@ mod tests { #[test] fn parse_with_super_prefix() { - let src = "use super::foo"; + let src = "use super::foo;"; let (mut module, errors) = parse_program(src); assert!(errors.is_empty()); assert_eq!(module.items.len(), 1); diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 1bbb81737b1..11fada32834 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,7 +1,6 @@ use crate::{ ast::{TraitBound, UnresolvedTraitConstraint}, - parser::ParserErrorReason, - token::Keyword, + token::{Keyword, Token}, }; use super::Parser; @@ -22,16 +21,14 @@ impl<'a> Parser<'a> { }; if !trailing_comma && !constraints.is_empty() { - self.push_error(ParserErrorReason::MissingCommaSeparatingTraitBounds, start_span); + self.expected_token_separating_items(",", "trait bounds", start_span); } - if self.eat_colon() { - let trait_bounds = self.parse_trait_bounds(); - for trait_bound in trait_bounds { - constraints.push(UnresolvedTraitConstraint { typ: typ.clone(), trait_bound }); - } - } else { - // TODO: error + self.eat_or_error(Token::Colon); + + let trait_bounds = self.parse_trait_bounds(); + for trait_bound in trait_bounds { + constraints.push(UnresolvedTraitConstraint { typ: typ.clone(), trait_bound }); } trailing_comma = self.eat_commas(); @@ -56,7 +53,7 @@ impl<'a> Parser<'a> { } if !trailing_plus && !bounds.is_empty() { - self.push_error(ParserErrorReason::MissingPlusSeparatingTraitBounds, start_span); + self.expected_token_separating_items("+", "trait bounds", start_span); } bounds.push(bound); From 23a3afde19c27603c65b3a49a623bf4d83a32c72 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 08:20:00 -0300 Subject: [PATCH 147/229] A bit more --- .../noirc_frontend/src/parser/parser/impls.rs | 4 ++-- .../src/parser/parser/traits.rs | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index b6bc81b6bc9..056cd6fb9dd 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -58,7 +58,7 @@ impl<'a> Parser<'a> { } if self.is_eof() { - // TODO: error + self.expected_token(Token::RightBrace); break; } @@ -429,7 +429,7 @@ mod tests { fn parse_empty_impl_missing_right_brace() { let src = "impl Foo {"; let (module, errors) = parse_program(src); - assert_eq!(errors.len(), 0); // TODO: this should be 1 + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Impl(type_impl) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 28dfa232fa9..a0f10ea2bf9 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -48,22 +48,25 @@ impl<'a> Parser<'a> { } loop { + if self.eat_right_brace() { + break; + } + + if self.is_eof() { + self.expected_token(Token::RightBrace); + break; + } + let doc_comments = self.parse_outer_doc_comments(); if let Some(item) = self.parse_trait_item() { items.push(Documented::new(item, doc_comments)); + continue; + } - if self.eat_right_brace() { - break; - } - } else { + if self.token.token() != &Token::RightBrace { // TODO: error - if self.is_eof() || self.eat_right_brace() { - break; - } else { - // Keep going - self.next_token(); - } + self.next_token(); } } From 4ca22f176fbf3c9ed5dba601f89bcfe0ecbd2803 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 10:07:27 -0300 Subject: [PATCH 148/229] Sometimes allow trailing colons in path --- .../noirc_frontend/src/parser/parser/path.rs | 28 +++++++++++++------ .../src/parser/parser/use_tree.rs | 1 + 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 7ba9f3f4b90..ff2754e443c 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -26,6 +26,7 @@ impl<'a> Parser<'a> { pub(crate) fn parse_path(&mut self) -> Option { self.parse_path_impl( true, // allow turbofish + true, // allow trailing double colon ) } @@ -45,10 +46,15 @@ impl<'a> Parser<'a> { pub(crate) fn parse_path_no_turbofish(&mut self) -> Option { self.parse_path_impl( false, // allow turbofish + true, // allow trailing double colon ) } - pub(super) fn parse_path_impl(&mut self, allow_turbofish: bool) -> Option { + 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(); @@ -56,13 +62,14 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::DoubleColon); } - self.parse_path_after_kind(kind, allow_turbofish, start_span) + self.parse_path_after_kind(kind, allow_turbofish, allow_trailing_double_colon, start_span) } pub(super) fn parse_path_after_kind( &mut self, kind: PathKind, allow_turbofish: bool, + allow_trailing_double_colon: bool, start_span: Span, ) -> Option { let mut segments = Vec::new(); @@ -90,6 +97,11 @@ impl<'a> Parser<'a> { // Skip the double colons self.next_token(); } else { + if allow_trailing_double_colon && self.eat_double_colon() { + self.expected_identifier(); + break; + } + break; } } @@ -231,8 +243,8 @@ mod tests { 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() - 2); - assert_eq!(parser.errors.len(), 0); // TODO: this should be 1 + 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"); @@ -261,8 +273,8 @@ mod tests { 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() - 2); - assert!(parser.errors.is_empty()); + assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar"); } @@ -271,8 +283,8 @@ mod tests { 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() - 2); - assert!(parser.errors.is_empty()); + assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar::<1>"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index e30e1988dcc..f7cb27dcdf2 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -30,6 +30,7 @@ impl<'a> Parser<'a> { ) -> UseTree { let prefix = self.parse_path_after_kind( kind, false, // allow turbofish + false, // allow trailing double colon start_span, ); let prefix = prefix.unwrap_or_else(|| Path { From e1b242a3f6667765774cd957936ca03a6260574f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 10:10:01 -0300 Subject: [PATCH 149/229] Fix LSP test --- tooling/lsp/src/requests/completion/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/lsp/src/requests/completion/tests.rs b/tooling/lsp/src/requests/completion/tests.rs index 45eb79bd1c2..c361dfc4bb9 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; From 62b6673e0d371438415e5a0bb053ffe599d041bb Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 10:19:38 -0300 Subject: [PATCH 150/229] Simplify function body parsing --- compiler/noirc_frontend/src/parser/parser/function.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 8fdf90920b1..5e25d1d87e3 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -97,17 +97,14 @@ impl<'a> Parser<'a> { let where_clause = self.parse_where_clause(); let body_start_span = self.current_token_span; - let (body, body_span) = if self.eat_semicolons() { + let body = if self.eat_semicolons() { if !allow_optional_body { self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_span); } - (None, Span::from(body_start_span.end()..body_start_span.end())) + None } else { - ( - Some(self.parse_block_expression().unwrap_or_else(empty_body)), - self.span_since(body_start_span), - ) + Some(self.parse_block_expression().unwrap_or_else(empty_body)) }; FunctionDefinitionWithOptionalBody { @@ -115,7 +112,7 @@ impl<'a> Parser<'a> { generics, parameters, body, - span: body_span, + span: self.span_since(body_start_span), where_clause, return_type, return_visibility, From 7d597b260477e276e714b45d205f63bacc550e41 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 10:29:33 -0300 Subject: [PATCH 151/229] Adjust statement span to include the semicolon --- compiler/noirc_frontend/src/parser/parser/expression.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 7b7ea81f01f..89cea37d077 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -618,7 +618,7 @@ impl<'a> Parser<'a> { break; } - let Some(statement) = self.parse_statement() else { + let Some(mut statement) = self.parse_statement() else { // TODO: error? self.eat_right_brace(); break; @@ -628,6 +628,10 @@ impl<'a> Parser<'a> { let token = self.token.clone(); self.next_token(); let span = token.to_span(); + + // Adjust the statement span to include the semicolon + statement.span = Span::from(statement.span.start()..span.end()); + (Some(token.into_token()), span) } else { (None, self.previous_token_span) From 7b9558acaa50f2d6de52a2172072dc219a483c25 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 11:16:33 -0300 Subject: [PATCH 152/229] Fix span of `mut self` and `&mut self` patterns --- compiler/noirc_frontend/src/parser/parser/function.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 5e25d1d87e3..fcec96db7fc 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -196,17 +196,16 @@ impl<'a> Parser<'a> { if self_pattern.reference { self_type = UnresolvedTypeData::MutableReference(Box::new(self_type)) - .with_span(self.span_since(start_span)); + .with_span(span); } else if self_pattern.mutable { - pattern = - Pattern::Mutable(Box::new(pattern), self.span_since(start_span), true); + pattern = Pattern::Mutable(Box::new(pattern), span, true); } parameters.push(Param { visibility: Visibility::Private, pattern, typ: self_type, - span: self.span_since(start_span), + span, }); } } From b6871ac74954b8de6b3d671709b88ec3e8ec87bf Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 11:31:18 -0300 Subject: [PATCH 153/229] Some span fixes --- .../src/parser/parser/expression.rs | 4 +- .../src/parser/parser/statement.rs | 42 +++++++++++-------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 89cea37d077..feeee51f3ee 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -380,12 +380,12 @@ impl<'a> Parser<'a> { } fn parse_comptime_expr(&mut self) -> Option { - let start_span = self.current_token_span; - if !self.eat_keyword(Keyword::Comptime) { return None; } + let start_span = self.current_token_span; + let Some(block) = self.parse_block_expression() else { self.expected_token(Token::LeftBrace); return None; diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 7171591b3cd..be794525089 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -221,39 +221,45 @@ impl<'a> Parser<'a> { &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), + }))); + } + + // TODO: error (found comptime but not a valid statement) + + 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_expression() { - let span = self.span_since(start_span); - return Some(StatementKind::Comptime(Box::new(Statement { - kind: StatementKind::Expression(Expression::new( - ExpressionKind::Block(block), - span, - )), - span, - }))); + 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::Comptime(Box::new(Statement { - kind: StatementKind::Let(let_statement), - span: self.span_since(start_span), - }))); + return Some(StatementKind::Let(let_statement)); } if let Some(for_loop) = self.parse_for() { - return Some(StatementKind::Comptime(Box::new(Statement { - kind: StatementKind::For(for_loop), - span: self.span_since(start_span), - }))); + return Some(StatementKind::For(for_loop)); } - // TODO: error (found comptime but not a valid statement) - None } From fb0dac9f3bf847d1cc6589405918dcb7af08894b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 11:33:04 -0300 Subject: [PATCH 154/229] Fix unsafe span --- compiler/noirc_frontend/src/parser/parser/expression.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index feeee51f3ee..7b14c7fa0db 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -279,7 +279,7 @@ impl<'a> Parser<'a> { return None; } - let start_span = self.span_since(self.previous_token_span); + let start_span = self.current_token_span; if let Some(block) = self.parse_block_expression() { Some(ExpressionKind::Unsafe(block, self.span_since(start_span))) } else { From 81edc43a69bb46da594f66174b4c0d7b4052264a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 11:39:11 -0300 Subject: [PATCH 155/229] All nargo fmt tests now pass --- tooling/nargo_fmt/tests/expected/databus.nr | 2 +- tooling/nargo_fmt/tests/input/databus.nr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/input/databus.nr b/tooling/nargo_fmt/tests/input/databus.nr index 60934b60b2f..0e9761ed52d 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 {} From 4cd0397f3485ea01eb809179793a46ebf1de6c2b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 11:44:53 -0300 Subject: [PATCH 156/229] Fix document symbol test --- tooling/lsp/src/requests/document_symbol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/lsp/src/requests/document_symbol.rs b/tooling/lsp/src/requests/document_symbol.rs index 5948d73a725..0fc2dc4622e 100644 --- a/tooling/lsp/src/requests/document_symbol.rs +++ b/tooling/lsp/src/requests/document_symbol.rs @@ -674,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 }, From 65d71db23b459cd970f10ac7c89e08bc74b64f84 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 12:04:17 -0300 Subject: [PATCH 157/229] More fixes --- .../src/hir/comptime/interpreter/builtin.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 16 ++++ .../src/parser/parser/expression.rs | 35 ++++---- .../src/parser/parser/function.rs | 14 ++-- .../src/parser/parser/lambda.rs | 11 +-- .../src/parser/parser/pattern.rs | 79 +++++++++++-------- .../src/parser/parser/statement.rs | 37 ++++++++- .../noirc_frontend/src/parser/parser/types.rs | 2 +- 8 files changed, 125 insertions(+), 71 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 68455879028..d01ac126831 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -2341,7 +2341,7 @@ fn function_def_set_parameters( let parameter_pattern = parse( interpreter.elaborator.interner, (tuple.pop().unwrap(), parameters_argument_location), - Parser::parse_pattern, + Parser::parse_pattern_or_error, "a pattern", )?; diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 9f963ee31fa..53c2d4b3601 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -54,6 +54,11 @@ where F: FnOnce(&mut Parser<'a>) -> T, { let item = f(&mut parser); + if !parser.is_eof() { + parser.expected_token(Token::EOF); + return Err(parser.errors); + } + if parser.errors.is_empty() { Ok(item) } else { @@ -129,6 +134,17 @@ impl<'a> Parser<'a> { } pub(crate) fn parse_lvalue_or_error(&mut self) -> LValue { + let start_span = self.current_token_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!(), + } + } + let expr = self.parse_expression_or_error(); if let Some(lvalue) = LValue::from_expression(expr) { lvalue diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 7b14c7fa0db..88ec0fdc67f 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -5,8 +5,7 @@ use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, Expression, ExpressionKind, GenericTypeArgs, Ident, IfExpression, IndexExpression, Literal, - MemberAccessExpression, MethodCallExpression, Path, Statement, TypePath, UnaryOp, - UnresolvedType, + MemberAccessExpression, MethodCallExpression, Statement, TypePath, UnaryOp, UnresolvedType, }, parser::ParserErrorReason, token::{Keyword, Token, TokenKind}, @@ -202,6 +201,16 @@ impl<'a> Parser<'a> { return Some(kind); } + if matches!(self.token.token(), Token::InternedUnresolvedTypeData(..)) + && self.next_token.token() == &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); } @@ -293,13 +302,14 @@ impl<'a> Parser<'a> { }; if allow_constructors && self.eat_left_brace() { - return Some(self.parse_constructor(path)); + let typ = UnresolvedType::from_path(path); + return Some(self.parse_constructor(typ)); } Some(ExpressionKind::Variable(path)) } - fn parse_constructor(&mut self, path: Path) -> ExpressionKind { + fn parse_constructor(&mut self, typ: UnresolvedType) -> ExpressionKind { let mut fields = Vec::new(); let mut trailing_comma = false; @@ -329,7 +339,7 @@ impl<'a> Parser<'a> { } ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ: UnresolvedType::from_path(path), + typ, fields, struct_type: None, })) @@ -618,25 +628,12 @@ impl<'a> Parser<'a> { break; } - let Some(mut statement) = self.parse_statement() else { + let Some((statement, (token, span))) = self.parse_statement() else { // TODO: error? self.eat_right_brace(); break; }; - let (token, span) = if self.token.token() == &Token::Semicolon { - let token = self.token.clone(); - self.next_token(); - let span = token.to_span(); - - // Adjust the statement span to include the semicolon - statement.span = Span::from(statement.span.start()..span.end()); - - (Some(token.into_token()), span) - } else { - (None, self.previous_token_span) - }; - statements.push((statement, (token, span))); } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index fcec96db7fc..725537ccec2 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -137,17 +137,19 @@ impl<'a> Parser<'a> { let pattern_or_self = if allow_self && parameters.is_empty() { self.parse_pattern_or_self() } else { - PatternOrSelf::Pattern(self.parse_pattern()) + self.parse_pattern().map(PatternOrSelf::Pattern) }; - if self.current_token_span == start_span { - // An error was already produced by parse_pattern(). - // Let's try with the next token. + + let Some(pattern_or_self) = pattern_or_self else { + self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); + // Let's try with the next token self.next_token(); if self.is_eof() { break; + } else { + continue; } - continue; - } + }; if !trailing_comma && !parameters.is_empty() { self.expected_token_separating_items(",", "parameters", start_span); diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index 45de6e94b2b..fa036445c43 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -1,5 +1,6 @@ use crate::{ ast::{ExpressionKind, Lambda, Pattern, UnresolvedType}, + parser::ParserErrorReason, token::Token, }; @@ -32,16 +33,16 @@ impl<'a> Parser<'a> { } let start_span = self.current_token_span; - let pattern = self.parse_pattern(); - if self.current_token_span == start_span { - // An error was already produced by parse_pattern(). + let Some(pattern) = self.parse_pattern() else { + self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); // Let's try with the next token. self.next_token(); if self.is_eof() { break; + } else { + continue; } - continue; - } + }; if !trailing_comma && !parameters.is_empty() { self.expected_token_separating_items(",", "parameters", start_span); diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index be27fec4fb1..f64bff73ce2 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -19,13 +19,22 @@ pub(crate) struct SelfPattern { } impl<'a> Parser<'a> { - pub(crate) fn parse_pattern(&mut self) -> Pattern { + pub(crate) fn parse_pattern_or_error(&mut self) -> Pattern { + if let Some(pattern) = self.parse_pattern() { + return pattern; + } + + self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); + Pattern::Identifier(Ident::new(String::new(), self.span_at_previous_token_end())) + } + + 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) } - pub(crate) fn parse_pattern_or_self(&mut self) -> PatternOrSelf { + pub(crate) fn parse_pattern_or_self(&mut self) -> Option { let start_span = self.current_token_span; let reference = self.eat(Token::Ampersand); @@ -33,10 +42,10 @@ impl<'a> Parser<'a> { if self.eat_self() { // TODO: error if reference but not mutable - PatternOrSelf::SelfPattern(SelfPattern { reference, mutable }) + Some(PatternOrSelf::SelfPattern(SelfPattern { reference, mutable })) } else { // TODO: error if reference is true - PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(mutable, start_span)) + Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(mutable, start_span)?)) } } @@ -44,9 +53,9 @@ impl<'a> Parser<'a> { &mut self, mutable: bool, start_span: Span, - ) -> Pattern { - let pattern = self.parse_pattern_no_mut(); - if mutable { + ) -> Option { + let pattern = self.parse_pattern_no_mut()?; + Some(if mutable { Pattern::Mutable( Box::new(pattern), self.span_since(start_span), @@ -54,35 +63,34 @@ impl<'a> Parser<'a> { ) } else { pattern - } + }) } - fn parse_pattern_no_mut(&mut self) -> Pattern { + fn parse_pattern_no_mut(&mut self) -> Option { if let Some(pattern) = self.parse_interned_pattern() { - return pattern; + return Some(pattern); } if let Some(pattern) = self.parse_tuple_pattern() { - return pattern; + return Some(pattern); } let Some(mut path) = self.parse_path() else { - self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); - return Pattern::Identifier(Ident::default()); + return None; }; if self.eat_left_brace() { - return self.parse_struct_pattern(path); + return Some(self.parse_struct_pattern(path)); } if !path.is_ident() { // TODO: error let ident = path.segments.pop().unwrap().ident; - return Pattern::Identifier(ident); + return Some(Pattern::Identifier(ident)); } let ident = path.segments.remove(0).ident; - Pattern::Identifier(ident) + Some(Pattern::Identifier(ident)) } fn parse_tuple_pattern(&mut self) -> Option { @@ -100,12 +108,11 @@ impl<'a> Parser<'a> { } let start_span = self.current_token_span; - let pattern = self.parse_pattern(); - if self.current_token_span == start_span { - // TODO: error + let Some(pattern) = self.parse_pattern() else { + self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); self.eat_right_paren(); break; - } + }; if !trailing_comma && !patterns.is_empty() { self.expected_token_separating_items(",", "tuple elements", start_span); @@ -143,7 +150,7 @@ impl<'a> Parser<'a> { } if self.eat_colon() { - patterns.push((ident, self.parse_pattern())); + patterns.push((ident, self.parse_pattern_or_error())); } else { patterns.push((ident.clone(), Pattern::Identifier(ident))); } @@ -177,9 +184,9 @@ mod tests { fn parses_identifier_pattern() { let src = "foo"; let mut parser = Parser::for_str(src); - let typ = parser.parse_pattern(); + let pattern = parser.parse_pattern_or_error(); assert!(parser.errors.is_empty()); - let Pattern::Identifier(ident) = typ else { panic!("Expected an identifier pattern") }; + let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; assert_eq!(ident.to_string(), "foo"); } @@ -187,9 +194,9 @@ mod tests { fn parses_mutable_pattern() { let src = "mut foo"; let mut parser = Parser::for_str(src); - let typ = parser.parse_pattern(); + let pattern = parser.parse_pattern_or_error(); assert!(parser.errors.is_empty()); - let Pattern::Mutable(pattern, _, _) = typ else { panic!("Expected a mutable pattern") }; + 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"); @@ -199,9 +206,9 @@ mod tests { fn parses_tuple_pattern() { let src = "(foo, bar)"; let mut parser = Parser::for_str(src); - let typ = parser.parse_pattern(); + let pattern = parser.parse_pattern_or_error(); assert!(parser.errors.is_empty()); - let Pattern::Tuple(mut patterns, _) = typ else { panic!("Expected a tuple pattern") }; + let Pattern::Tuple(mut patterns, _) = pattern else { panic!("Expected a tuple pattern") }; assert_eq!(patterns.len(), 2); let pattern = patterns.remove(0); @@ -217,9 +224,9 @@ mod tests { fn parses_unclosed_tuple_pattern() { let src = "(foo,"; let mut parser = Parser::for_str(src); - let typ = parser.parse_pattern(); + let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); - let Pattern::Tuple(patterns, _) = typ else { panic!("Expected a tuple pattern") }; + let Pattern::Tuple(patterns, _) = pattern else { panic!("Expected a tuple pattern") }; assert_eq!(patterns.len(), 1); } @@ -227,9 +234,11 @@ mod tests { fn parses_struct_pattern_no_fields() { let src = "foo::Bar {}"; let mut parser = Parser::for_str(src); - let typ = parser.parse_pattern(); + let pattern = parser.parse_pattern_or_error(); assert!(parser.errors.is_empty()); - let Pattern::Struct(path, patterns, _) = typ else { panic!("Expected a struct pattern") }; + let Pattern::Struct(path, patterns, _) = pattern else { + panic!("Expected a struct pattern") + }; assert_eq!(path.to_string(), "foo::Bar"); assert!(patterns.is_empty()); } @@ -238,9 +247,9 @@ mod tests { fn parses_struct_pattern() { let src = "foo::Bar { x: one, y }"; let mut parser = Parser::for_str(src); - let typ = parser.parse_pattern(); + let pattern = parser.parse_pattern_or_error(); assert!(parser.errors.is_empty()); - let Pattern::Struct(path, mut patterns, _) = typ else { + let Pattern::Struct(path, mut patterns, _) = pattern else { panic!("Expected a struct pattern") }; assert_eq!(path.to_string(), "foo::Bar"); @@ -259,9 +268,9 @@ mod tests { fn parses_unclosed_struct_pattern() { let src = "foo::Bar { x"; let mut parser = Parser::for_str(src); - let typ = parser.parse_pattern(); + let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); - let Pattern::Struct(path, _, _) = typ else { panic!("Expected a struct pattern") }; + let Pattern::Struct(path, _, _) = pattern else { panic!("Expected a struct pattern") }; assert_eq!(path.to_string(), "foo::Bar"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index be794525089..8095f53559f 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -14,7 +14,7 @@ use super::Parser; impl<'a> Parser<'a> { pub(crate) fn parse_statement_or_error(&mut self) -> Statement { - if let Some(statement) = self.parse_statement() { + if let Some((statement, (_token, _span))) = self.parse_statement() { statement } else { self.push_error( @@ -25,13 +25,28 @@ impl<'a> Parser<'a> { } } - pub(crate) fn parse_statement(&mut self) -> Option { + pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { let attributes = self.parse_attributes(); let start_span = self.current_token_span; let kind = self.parse_statement_kind(attributes)?; let span = self.span_since(start_span); - Some(Statement { kind, span }) + let mut statement = Statement { kind, span }; + + let (token, span) = if self.token.token() == &Token::Semicolon { + let token = self.token.clone(); + self.next_token(); + let span = token.to_span(); + + // Adjust the statement span to include the semicolon + statement.span = Span::from(statement.span.start()..span.end()); + + (Some(token.into_token()), span) + } else { + (None, self.previous_token_span) + }; + + Some((statement, (token, span))) } fn parse_statement_kind( @@ -94,6 +109,20 @@ impl<'a> Parser<'a> { })); } + 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)); + if !self.eat_assign() { + // TODO: error + } + let expression = self.parse_expression_or_error(); + return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); + } + _ => unreachable!(), + } + } + let expression = self.parse_expression()?; if self.eat_assign() { @@ -269,7 +298,7 @@ impl<'a> Parser<'a> { } let attributes = self.validate_secondary_attributes(attributes); - let pattern = self.parse_pattern(); + 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() diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 5be82fa2896..fbcb3e7fdad 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -333,7 +333,7 @@ impl<'a> Parser<'a> { None } - fn parse_interned_type(&mut self) -> Option { + 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) => { From 51cda478346ec3b5004965eb4db74a163a5e8a63 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 14:09:40 -0300 Subject: [PATCH 158/229] Don't rely on spans for parsing --- compiler/noirc_frontend/src/ast/traits.rs | 19 +++++++++++- .../src/hir/comptime/interpreter/builtin.rs | 2 +- compiler/noirc_frontend/src/parser/errors.rs | 2 ++ .../src/parser/parser/where_clause.rs | 31 ++++++++++++++----- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index 3df9939dc70..25128c6440b 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/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index d01ac126831..1c2d3e4b716 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -735,7 +735,7 @@ fn quoted_as_trait_constraint( let trait_bound = parse( interpreter.elaborator.interner, argument, - Parser::parse_trait_bound, + Parser::parse_trait_bound_or_error, "a trait constraint", )?; let bound = interpreter diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 8db72b4926e..5c7a7d6f0ba 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -27,6 +27,8 @@ pub enum ParserErrorReason { ExpectedItem { found: Token }, #[error("Invalid left-hand side of assignment")] InvalidLeftHandSideOfAssignment, + #[error("Expected trait bound")] + ExpectedTraitBound, #[error("Missing type for function parameter")] MissingTypeForFunctionParameter, diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 11fada32834..18830c1e8a3 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,5 +1,6 @@ use crate::{ - ast::{TraitBound, UnresolvedTraitConstraint}, + ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint}, + parser::ParserErrorReason, token::{Keyword, Token}, }; @@ -47,10 +48,9 @@ impl<'a> Parser<'a> { let mut trailing_plus = false; loop { let start_span = self.current_token_span; - let bound = self.parse_trait_bound(); - if self.current_token_span == start_span { + let Some(bound) = self.parse_trait_bound() else { break; - } + }; if !trailing_plus && !bounds.is_empty() { self.expected_token_separating_items("+", "trait bounds", start_span); @@ -64,10 +64,27 @@ impl<'a> Parser<'a> { bounds } - pub(crate) fn parse_trait_bound(&mut self) -> TraitBound { - let trait_path = self.parse_path_no_turbofish_or_error(); + pub(crate) fn parse_trait_bound_or_error(&mut self) -> TraitBound { + if let Some(trait_bound) = self.parse_trait_bound() { + return trait_bound; + } + + self.push_error(ParserErrorReason::ExpectedTraitBound, self.current_token_span); + TraitBound { + trait_path: Path { + kind: PathKind::Plain, + segments: Vec::new(), + span: self.span_at_previous_token_end(), + }, + trait_id: None, + trait_generics: GenericTypeArgs::default(), + } + } + + 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(); - TraitBound { trait_path, trait_generics, trait_id: None } + Some(TraitBound { trait_path, trait_generics, trait_id: None }) } } From 0996a29b15fcbec3781e306585c56ee741b6c257 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 14:27:34 -0300 Subject: [PATCH 159/229] Parser type or type expression tuple of size 1 --- .../src/parser/parser/type_expression.rs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 2b13c1cea1b..3f012338c13 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -332,7 +332,10 @@ impl<'a> Parser<'a> { // TODO: error (missing comma separating tuple types) } - let typ = self.parse_type_or_error(); + let Some(typ) = self.parse_type() else { + self.eat_or_error(Token::RightParen); + break; + }; types.push(typ); if self.eat_right_paren() { @@ -563,6 +566,21 @@ mod tests { }; } + #[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(); + assert!(parser.errors.is_empty()); + 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"; From a6030c3e7608998261bfbd54a2db295d023e969e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 14:42:51 -0300 Subject: [PATCH 160/229] Handle a few more errors --- compiler/noirc_frontend/src/parser/parser/generics.rs | 1 - compiler/noirc_frontend/src/parser/parser/impls.rs | 2 +- compiler/noirc_frontend/src/parser/parser/statement.rs | 4 +--- compiler/noirc_frontend/src/parser/parser/type_alias.rs | 2 +- compiler/noirc_frontend/src/parser/parser/type_expression.rs | 4 +--- compiler/noirc_frontend/src/parser/parser/types.rs | 4 ++-- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 342d82a9c76..223060c3172 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -20,7 +20,6 @@ impl<'a> Parser<'a> { } if self.eat_greater() { - // TODO: error? (for later) return generics; } diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 056cd6fb9dd..e2244503564 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -218,7 +218,7 @@ impl<'a> Parser<'a> { let name = match self.eat_ident() { Some(name) => name, None => { - // TODO: error + self.expected_identifier(); Ident::default() } }; diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 8095f53559f..f0c046358cd 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -113,9 +113,7 @@ impl<'a> Parser<'a> { match token.into_token() { Token::InternedLValue(lvalue) => { let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); - if !self.eat_assign() { - // TODO: error - } + self.eat_or_error(Token::Assign); let expression = self.parse_expression_or_error(); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); } diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 38e5eb3c088..0f59afc0126 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -38,7 +38,7 @@ impl<'a> Parser<'a> { let typ = self.parse_type_or_error(); let span = self.span_since(start_span); if !self.eat_semicolons() { - // TODO: error? (missing semicolon after type alias declaration) + self.expected_token(Token::Semicolon); } NoirTypeAlias { name, generics, typ, span } diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 3f012338c13..c2060a7c43a 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -310,9 +310,7 @@ impl<'a> Parser<'a> { let typ_span = typ.span; if let UnresolvedTypeData::Expression(type_expr) = typ.typ { - if !self.eat_right_paren() { - // TODO: error (expected `)`) - } + self.eat_or_error(Token::RightParen); return Some(UnresolvedType { typ: UnresolvedTypeData::Expression(type_expr), span: typ_span, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index fbcb3e7fdad..02a27effb75 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -235,7 +235,7 @@ impl<'a> Parser<'a> { if !self.eat_keyword(Keyword::Fn) { if unconstrained { - // TODO: error (expected `fn` after `unconstrained`) + self.expected_token(Token::Keyword(Keyword::Fn)); return Some(UnresolvedTypeData::Function( Vec::new(), Box::new(self.unspecified_type_at_previous_token_end()), @@ -405,7 +405,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let Some(typ) = self.parse_type() else { - // TODO: error + // TODO: error if this is the first type self.eat_right_paren(); break; }; From bf5a85680239260348066ee8d3c92fc0532c685c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 15:43:24 -0300 Subject: [PATCH 161/229] Fix attributes parsing --- compiler/noirc_frontend/src/elaborator/comptime.rs | 13 +++++++++---- compiler/noirc_frontend/src/parser/parser.rs | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 10b640fc17d..314c5b23a0b 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -24,7 +24,7 @@ use crate::{ Expression, ExpressionKind, HirExpression, NodeInterner, SecondaryAttribute, StructId, }, node_interner::{DefinitionKind, DependencyId, FuncId, TraitId}, - parser::{parse_result, Item, ItemKind, Parser}, + parser::{Item, ItemKind, Parser}, Type, TypeBindings, UnificationError, }; @@ -262,9 +262,14 @@ impl<'context> Elaborator<'context> { return Err((lexing_errors.swap_remove(0).into(), location.file)); } - let parser = Parser::for_tokens(tokens); - let expression = parse_result(parser, Parser::parse_expression_or_error) - .map_err(|mut errors| (errors.swap_remove(0).into(), location.file))?; + let mut parser = Parser::for_tokens(tokens); + let expression = parser.parse_expression(); + if !parser.errors.is_empty() { + return Ok(None); + } + let Some(expression) = expression else { + return Ok(None); + }; let (mut func, mut arguments) = match expression.kind { ExpressionKind::Call(call) => (*call.func, call.arguments), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 53c2d4b3601..dd45e760ff2 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -81,7 +81,7 @@ impl<'a> TokenStream<'a> { } pub struct Parser<'a> { - errors: Vec, + pub(crate) errors: Vec, tokens: TokenStream<'a>, // We always have one look-ahead token for these cases: From a7884085c71dd668930984ff5f369b2076a1774c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 16:26:09 -0300 Subject: [PATCH 162/229] Use parsing rule label --- compiler/noirc_frontend/src/parser/errors.rs | 8 -------- compiler/noirc_frontend/src/parser/labels.rs | 6 ++++++ compiler/noirc_frontend/src/parser/parser.rs | 15 ++++++++++----- .../noirc_frontend/src/parser/parser/function.rs | 4 ++-- compiler/noirc_frontend/src/parser/parser/item.rs | 7 ++----- .../noirc_frontend/src/parser/parser/lambda.rs | 5 +++-- .../noirc_frontend/src/parser/parser/pattern.rs | 6 +++--- .../src/parser/parser/where_clause.rs | 4 ++-- 8 files changed, 28 insertions(+), 27 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 5c7a7d6f0ba..c4871a9c44c 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -13,22 +13,14 @@ use super::labels::ParsingRuleLabel; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum ParserErrorReason { - #[error("Expected pattern")] - ExpectedPattern, #[error("Unexpected `;`")] UnexpectedSemicolon, #[error("Unexpected `,`")] UnexpectedComma, #[error("Expected a `{token}` separating these two {items}")] ExpectedTokenSeparatingTwoItems { token: String, items: String }, - #[error("Expected an identifier, found {found}")] - ExpectedIdentifier { found: Token }, - #[error("Expected a item, found {found}")] - ExpectedItem { found: Token }, #[error("Invalid left-hand side of assignment")] InvalidLeftHandSideOfAssignment, - #[error("Expected trait bound")] - ExpectedTraitBound, #[error("Missing type for function parameter")] MissingTypeForFunctionParameter, diff --git a/compiler/noirc_frontend/src/parser/labels.rs b/compiler/noirc_frontend/src/parser/labels.rs index fd082dfbe56..430aabacde4 100644 --- a/compiler/noirc_frontend/src/parser/labels.rs +++ b/compiler/noirc_frontend/src/parser/labels.rs @@ -12,11 +12,14 @@ pub enum ParsingRuleLabel { Expression, FieldAccess, Global, + Identifier, IntegerType, + Item, Parameter, Pattern, Statement, Term, + TraitBound, TypeExpression, TokenKind(TokenKind), } @@ -30,11 +33,14 @@ impl fmt::Display for ParsingRuleLabel { ParsingRuleLabel::Expression => write!(f, "expression"), ParsingRuleLabel::FieldAccess => write!(f, "field access"), ParsingRuleLabel::Global => write!(f, "global"), + ParsingRuleLabel::Identifier => write!(f, "identifier"), ParsingRuleLabel::IntegerType => write!(f, "integer type"), + ParsingRuleLabel::Item => write!(f, "item"), ParsingRuleLabel::Parameter => write!(f, "parameter"), ParsingRuleLabel::Pattern => write!(f, "pattern"), ParsingRuleLabel::Statement => write!(f, "statement"), ParsingRuleLabel::Term => write!(f, "term"), + ParsingRuleLabel::TraitBound => write!(f, "trait bound"), ParsingRuleLabel::TypeExpression => write!(f, "type expression"), ParsingRuleLabel::TokenKind(token_kind) => write!(f, "{token_kind:?}"), } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index dd45e760ff2..7c2016967b1 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -7,7 +7,7 @@ use crate::{ token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; -use super::{ParsedModule, ParserError, ParserErrorReason}; +use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReason}; mod attributes; mod call; @@ -445,10 +445,7 @@ impl<'a> Parser<'a> { } fn expected_identifier(&mut self) { - self.push_error( - ParserErrorReason::ExpectedIdentifier { found: self.token.token().clone() }, - self.current_token_span, - ); + self.expected_label(ParsingRuleLabel::Identifier); } fn expected_token(&mut self, token: Token) { @@ -459,6 +456,14 @@ impl<'a> Parser<'a> { )) } + fn expected_label(&mut self, label: ParsingRuleLabel) { + self.errors.push(ParserError::expected_label( + label, + self.token.token().clone(), + self.current_token_span, + )) + } + fn expected_token_separating_items(&mut self, token: &str, items: &str, span: Span) { self.push_error( ParserErrorReason::ExpectedTokenSeparatingTwoItems { diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 725537ccec2..6652bcf0043 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -7,7 +7,7 @@ use crate::{ ItemVisibility, NoirFunction, Param, Path, Pattern, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }, - parser::ParserErrorReason, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Attribute, Attributes, Keyword, Token}, }; @@ -141,7 +141,7 @@ impl<'a> Parser<'a> { }; let Some(pattern_or_self) = pattern_or_self else { - self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); + self.expected_label(ParsingRuleLabel::Pattern); // Let's try with the next token self.next_token(); if self.is_eof() { diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 9282dd82ffd..833b4f542a3 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -1,5 +1,5 @@ use crate::{ - parser::{Item, ItemKind, ParserErrorReason}, + parser::{labels::ParsingRuleLabel, Item, ItemKind}, token::{Keyword, Token}, }; @@ -36,10 +36,7 @@ impl<'a> Parser<'a> { }; if is_error_token { - self.push_error( - ParserErrorReason::ExpectedItem { found: self.token.token().clone() }, - self.current_token_span, - ); + self.expected_label(ParsingRuleLabel::Item); // We'll try parsing an item on the next token self.next_token(); } diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index fa036445c43..dd8d2bedd07 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -1,6 +1,6 @@ use crate::{ ast::{ExpressionKind, Lambda, Pattern, UnresolvedType}, - parser::ParserErrorReason, + parser::labels::ParsingRuleLabel, token::Token, }; @@ -34,7 +34,8 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let Some(pattern) = self.parse_pattern() else { - self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); + self.expected_label(ParsingRuleLabel::Pattern); + // Let's try with the next token. self.next_token(); if self.is_eof() { diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index f64bff73ce2..29905bda633 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, Path, Pattern}, - parser::ParserErrorReason, + parser::labels::ParsingRuleLabel, token::{Keyword, Token, TokenKind}, }; @@ -24,7 +24,7 @@ impl<'a> Parser<'a> { return pattern; } - self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); + self.expected_label(ParsingRuleLabel::Pattern); Pattern::Identifier(Ident::new(String::new(), self.span_at_previous_token_end())) } @@ -109,7 +109,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let Some(pattern) = self.parse_pattern() else { - self.push_error(ParserErrorReason::ExpectedPattern, self.current_token_span); + self.expected_label(ParsingRuleLabel::Pattern); self.eat_right_paren(); break; }; diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 18830c1e8a3..8f93b9bac54 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,6 +1,6 @@ use crate::{ ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint}, - parser::ParserErrorReason, + parser::labels::ParsingRuleLabel, token::{Keyword, Token}, }; @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { return trait_bound; } - self.push_error(ParserErrorReason::ExpectedTraitBound, self.current_token_span); + self.expected_label(ParsingRuleLabel::TraitBound); TraitBound { trait_path: Path { kind: PathKind::Plain, From 86976cee82fc7eebbd7908a388af9cfcbf1e2aad Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 17:09:28 -0300 Subject: [PATCH 163/229] More errors --- compiler/noirc_frontend/src/parser/errors.rs | 24 ++++++- compiler/noirc_frontend/src/parser/labels.rs | 12 ++++ compiler/noirc_frontend/src/parser/parser.rs | 66 ++++++++++++++++++- .../src/parser/parser/expression.rs | 4 +- .../noirc_frontend/src/parser/parser/impls.rs | 26 +++++--- .../noirc_frontend/src/parser/parser/item.rs | 28 +++++--- .../src/parser/parser/modifiers.rs | 5 +- .../noirc_frontend/src/parser/parser/path.rs | 8 ++- .../src/parser/parser/pattern.rs | 10 ++- .../src/parser/parser/statement.rs | 8 ++- .../src/parser/parser/traits.rs | 9 ++- .../src/parser/parser/type_expression.rs | 9 +-- .../noirc_frontend/src/parser/parser/types.rs | 9 ++- .../src/parser/parser/use_tree.rs | 2 +- 14 files changed, 178 insertions(+), 42 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index c4871a9c44c..ffa6759acb0 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; @@ -21,6 +21,20 @@ pub enum ParserErrorReason { ExpectedTokenSeparatingTwoItems { token: String, items: String }, #[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("Missing type for function parameter")] MissingTypeForFunctionParameter, @@ -157,6 +171,14 @@ impl ParserError { 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); diff --git a/compiler/noirc_frontend/src/parser/labels.rs b/compiler/noirc_frontend/src/parser/labels.rs index 430aabacde4..65719294c2e 100644 --- a/compiler/noirc_frontend/src/parser/labels.rs +++ b/compiler/noirc_frontend/src/parser/labels.rs @@ -15,12 +15,18 @@ pub enum ParsingRuleLabel { Identifier, IntegerType, Item, + LValue, Parameter, + Path, Pattern, Statement, Term, TraitBound, + TraitImplItem, + TraitItem, + Type, TypeExpression, + TypeOrTypeExpression, TokenKind(TokenKind), } @@ -36,12 +42,18 @@ impl fmt::Display for ParsingRuleLabel { ParsingRuleLabel::Identifier => write!(f, "identifier"), 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::TypeOrTypeExpression => write!(f, "type or type expression"), ParsingRuleLabel::TokenKind(token_kind) => write!(f, "{token_kind:?}"), } } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 7c2016967b1..be62443f36c 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1,8 +1,9 @@ use acvm::FieldElement; +use modifiers::Modifiers; use noirc_errors::Span; use crate::{ - ast::{Ident, LValue}, + ast::{Ident, ItemVisibility, LValue}, lexer::{Lexer, SpannedTokenResult}, token::{IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; @@ -149,7 +150,7 @@ impl<'a> Parser<'a> { if let Some(lvalue) = LValue::from_expression(expr) { lvalue } else { - // TODO: error (invalid l-value) + self.expected_label(ParsingRuleLabel::LValue); LValue::Ident(Ident::default()) } } @@ -456,6 +457,14 @@ impl<'a> Parser<'a> { )) } + 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, + )) + } + fn expected_label(&mut self, label: ParsingRuleLabel) { self.errors.push(ParserError::expected_label( label, @@ -474,6 +483,59 @@ impl<'a> Parser<'a> { ); } + 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); + } + + 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, + ); + } + } + + fn unconstrained_not_followed_by_an_item(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, span); + } + } + + fn comptime_not_followed_by_an_item(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, span); + } + } + + 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); + } + + fn mutable_not_applicable(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.mutable { + self.push_error(ParserErrorReason::MutableNotApplicable, span); + } + } + + fn comptime_not_applicable(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotApplicable, span); + } + } + + fn unconstrained_not_applicable(&mut self, modifiers: Modifiers) { + if let Some(span) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotApplicable, span); + } + } + fn push_error(&mut self, reason: ParserErrorReason, span: Span) { self.errors.push(ParserError::with_reason(reason, span)); } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 88ec0fdc67f..2b960c6c3a2 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -7,7 +7,7 @@ use crate::{ Expression, ExpressionKind, GenericTypeArgs, Ident, IfExpression, IndexExpression, Literal, MemberAccessExpression, MethodCallExpression, Statement, TypePath, UnaryOp, UnresolvedType, }, - parser::ParserErrorReason, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Keyword, Token, TokenKind}, }; @@ -629,7 +629,7 @@ impl<'a> Parser<'a> { } let Some((statement, (token, span))) = self.parse_statement() else { - // TODO: error? + self.expected_label(ParsingRuleLabel::Statement); self.eat_right_brace(); break; }; diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index e2244503564..37e4ca8cb8d 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -6,7 +6,7 @@ use crate::{ NoirFunction, NoirTraitImpl, Path, TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, - parser::ParserErrorReason, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Keyword, Token}, }; @@ -33,7 +33,12 @@ impl<'a> Parser<'a> { trait_name, )); } else { - // TODO: error, but we continue parsing the type and assume this is going to be a regular impl + 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(); }; } @@ -65,7 +70,9 @@ impl<'a> Parser<'a> { let doc_comments = self.parse_outer_doc_comments(); let start_span = self.current_token_span; let attributes = self.parse_attributes(); - let modifiers = self.parse_modifiers(); + let modifiers = self.parse_modifiers( + false, // allow mutable + ); if self.eat_keyword(Keyword::Fn) { let method = self.parse_function( @@ -79,10 +86,10 @@ impl<'a> Parser<'a> { continue; } - // TODO: error if visibility, unconstrained or comptime were found + self.modifiers_not_followed_by_an_item(modifiers); if self.token.token() != &Token::RightBrace { - // TODO: error + self.expected_token(Token::Keyword(Keyword::Fn)); self.next_token(); } } @@ -131,10 +138,11 @@ impl<'a> Parser<'a> { break; } } else { - // TODO: error if self.is_eof() || self.eat_right_brace() { break; } else { + self.expected_label(ParsingRuleLabel::TraitImplItem); + // Keep going self.next_token(); } @@ -161,7 +169,9 @@ impl<'a> Parser<'a> { } fn parse_trait_impl_function(&mut self) -> Option { - let modifiers = self.parse_modifiers(); + let modifiers = self.parse_modifiers( + false, // allow mut + ); if modifiers.visibility != ItemVisibility::Private { self.push_error( ParserErrorReason::TraitImplVisibilityIgnored, @@ -171,7 +181,7 @@ impl<'a> Parser<'a> { let attributes = self.parse_attributes(); if !self.eat_keyword(Keyword::Fn) { - // TODO: error if unconstrained, visibility or comptime + self.modifiers_not_followed_by_an_item(modifiers); return None; } diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 833b4f542a3..41d656c8ac4 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -62,16 +62,20 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let attributes = self.parse_attributes(); - let modifiers = self.parse_modifiers(); + let modifiers = self.parse_modifiers( + true, // allow mut + ); if self.eat_keyword(Keyword::Use) { - // TODO: error if there's comptime, mutable or unconstrained + 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() { - // TODO: error if there's comptime, mutable or unconstrained + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + return Some(self.parse_module_or_contract( attributes, is_contract, @@ -80,7 +84,8 @@ impl<'a> Parser<'a> { } if self.eat_keyword(Keyword::Struct) { - // TODO: error if there's comptime or mutable or unconstrained + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + return Some(ItemKind::Struct(self.parse_struct( attributes, modifiers.visibility, @@ -89,7 +94,8 @@ impl<'a> Parser<'a> { } if self.eat_keyword(Keyword::Impl) { - // TODO: error if there's comptime or mutable or unconstrained + 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), @@ -97,7 +103,8 @@ impl<'a> Parser<'a> { } if self.eat_keyword(Keyword::Trait) { - // TODO: error if there's comptime or mutable or unconstrained + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + return Some(ItemKind::Trait(self.parse_trait( attributes, modifiers.visibility, @@ -106,7 +113,8 @@ impl<'a> Parser<'a> { } if self.eat_keyword(Keyword::Global) { - // TODO: error if there's unconstrained + self.unconstrained_not_applicable(modifiers); + return Some(ItemKind::Global( self.parse_global( attributes, @@ -118,14 +126,16 @@ impl<'a> Parser<'a> { } if self.eat_keyword(Keyword::Type) { - // TODO: error if there's comptime or mutable or unconstrained + 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) { - // TODO: error if there's mutable + self.mutable_not_applicable(modifiers); + return Some(ItemKind::Function(self.parse_function( attributes, modifiers.visibility, diff --git a/compiler/noirc_frontend/src/parser/parser/modifiers.rs b/compiler/noirc_frontend/src/parser/parser/modifiers.rs index 6975f3e5afb..e74a3038bbf 100644 --- a/compiler/noirc_frontend/src/parser/parser/modifiers.rs +++ b/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -4,6 +4,7 @@ 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, @@ -13,7 +14,7 @@ pub(crate) struct Modifiers { } impl<'a> Parser<'a> { - pub(crate) fn parse_modifiers(&mut self) -> Modifiers { + pub(crate) fn parse_modifiers(&mut self, allow_mutable: bool) -> Modifiers { let unconstrained = self.parse_modifier(Keyword::Unconstrained); let start_span = self.current_token_span; @@ -21,7 +22,7 @@ impl<'a> Parser<'a> { let visibility_span = self.span_since(start_span); let comptime = self.parse_modifier(Keyword::Comptime); - let mutable = self.parse_modifier(Keyword::Mut); + let mutable = if allow_mutable { self.parse_modifier(Keyword::Mut) } else { None }; Modifiers { visibility, visibility_span, unconstrained, comptime, mutable } } diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 8c4c5410669..ca5581b3d24 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{AsTraitPath, Ident, Path, PathKind, PathSegment, UnresolvedType}, - parser::ParserErrorReason, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Keyword, Token, TokenKind}, }; @@ -14,7 +14,8 @@ impl<'a> Parser<'a> { if let Some(path) = self.parse_path() { path } else { - // TODO: error (expected path) + self.expected_label(ParsingRuleLabel::Path); + Path { segments: Vec::new(), kind: PathKind::Plain, @@ -34,7 +35,8 @@ impl<'a> Parser<'a> { if let Some(path) = self.parse_path_no_turbofish() { path } else { - // TODO: error (expected path) + self.expected_label(ParsingRuleLabel::Path); + Path { segments: Vec::new(), kind: PathKind::Plain, diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 29905bda633..a06f93a76f1 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -41,10 +41,16 @@ impl<'a> Parser<'a> { let mutable = self.eat_keyword(Keyword::Mut); if self.eat_self() { - // TODO: error if reference but not mutable + if reference && !mutable { + // TODO: error + } + Some(PatternOrSelf::SelfPattern(SelfPattern { reference, mutable })) } else { - // TODO: error if reference is true + if reference { + // TODO: error + } + Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(mutable, start_span)?)) } } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index f0c046358cd..f978ce3ddf6 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -261,7 +261,11 @@ impl<'a> Parser<'a> { }))); } - // TODO: error (found comptime but not a valid statement) + self.expected_one_of_tokens(&[ + Token::LeftBrace, + Token::Keyword(Keyword::Let), + Token::Keyword(Keyword::For), + ]); None } @@ -318,7 +322,7 @@ impl<'a> Parser<'a> { ConstrainKind::Assert | ConstrainKind::AssertEq => { let arguments = self.parse_arguments(); if arguments.is_none() { - // TODO: error (expected arguments to assert/assert_eq) + self.expected_token(Token::LeftParen); } let arguments = arguments.unwrap_or_default(); diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index a0f10ea2bf9..6808fa39a51 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -5,6 +5,7 @@ use crate::{ Documented, Ident, ItemVisibility, NoirTrait, Pattern, TraitItem, UnresolvedType, UnresolvedTypeData, }, + parser::labels::ParsingRuleLabel, token::{Attribute, Keyword, SecondaryAttribute, Token}, }; @@ -65,7 +66,7 @@ impl<'a> Parser<'a> { } if self.token.token() != &Token::RightBrace { - // TODO: error + self.expected_label(ParsingRuleLabel::TraitItem); self.next_token(); } } @@ -136,10 +137,12 @@ impl<'a> Parser<'a> { } fn parse_trait_function(&mut self) -> Option { - let modifiers = self.parse_modifiers(); + let modifiers = self.parse_modifiers( + false, // allow mut + ); if !self.eat_keyword(Keyword::Fn) { - // TODO: error if unconstrained, visibility or comptime + self.modifiers_not_followed_by_an_item(modifiers); return None; } diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index c2060a7c43a..cd3847afdfd 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -3,7 +3,7 @@ use crate::{ Expression, ExpressionKind, GenericTypeArgs, Literal, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }, - parser::{ParserError, ParserErrorReason}, + parser::{labels::ParsingRuleLabel, ParserError, ParserErrorReason}, token::Token, BinaryTypeOperator, }; @@ -304,11 +304,11 @@ impl<'a> Parser<'a> { } let Some(typ) = self.parse_type_or_type_expression() else { - // TODO: error + self.expected_label(ParsingRuleLabel::TypeOrTypeExpression); return None; }; - let typ_span = typ.span; + let mut typ_span = typ.span; if let UnresolvedTypeData::Expression(type_expr) = typ.typ { self.eat_or_error(Token::RightParen); return Some(UnresolvedType { @@ -327,13 +327,14 @@ impl<'a> Parser<'a> { let mut types = vec![typ]; loop { if !self.eat_commas() { - // TODO: error (missing comma separating tuple types) + self.expected_token_separating_items(",", "tuple items", typ_span); } let Some(typ) = self.parse_type() else { self.eat_or_error(Token::RightParen); break; }; + typ_span = typ.span; types.push(typ); if self.eat_right_paren() { diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 02a27effb75..349b572c6f1 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,6 +1,6 @@ use crate::{ ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::ParserErrorReason, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Keyword, Token, TokenKind}, QuotedType, }; @@ -306,7 +306,7 @@ impl<'a> Parser<'a> { } let Some(path) = self.parse_path_no_turbofish() else { - // TODO: error (expected path after impl) + self.expected_label(ParsingRuleLabel::Path); return None; }; @@ -405,7 +405,10 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let Some(typ) = self.parse_type() else { - // TODO: error if this is the first type + if types.is_empty() { + self.expected_label(ParsingRuleLabel::Type); + } + self.eat_right_paren(); break; }; diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index f7cb27dcdf2..2670b60cb72 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -92,7 +92,7 @@ impl<'a> Parser<'a> { if let Some(alias) = self.eat_ident() { UseTree { prefix, kind: UseTreeKind::Path(ident, Some(alias)) } } else { - // TODO: error + self.expected_identifier(); UseTree { prefix, kind: UseTreeKind::Path(ident, None) } } } else { From 9ed011354d85ddefe10f7853b4cddc11b5b42255 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 17:09:46 -0300 Subject: [PATCH 164/229] Remove TODO --- compiler/noirc_frontend/src/parser/parser/impls.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 37e4ca8cb8d..100c18e2fcb 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -126,7 +126,6 @@ impl<'a> Parser<'a> { } loop { - // TODO: maybe require visibility to always come first let start_span = self.current_token_span; let doc_comments = self.parse_outer_doc_comments(); From 7cde89eb26e902db97030523cbaeddedfd8486b3 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 17:19:15 -0300 Subject: [PATCH 165/229] More errors --- compiler/noirc_frontend/src/parser/parser/expression.rs | 2 +- compiler/noirc_frontend/src/parser/parser/pattern.rs | 6 +++--- compiler/noirc_frontend/src/parser/parser/traits.rs | 2 +- compiler/noirc_frontend/src/parser/parser/types.rs | 7 +++++-- compiler/noirc_frontend/src/parser/parser/use_tree.rs | 2 +- compiler/noirc_frontend/src/parser/parser/where_clause.rs | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 2b960c6c3a2..0030567fb62 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -454,7 +454,7 @@ impl<'a> Parser<'a> { let turbofish = if self.eat_double_colon() { let generics = self.parse_generic_type_args(); if generics.is_empty() { - // TODO: error + // TODO: error? (found `::` but not `::<...>`) } generics } else { diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index a06f93a76f1..daf60d68006 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -42,13 +42,13 @@ impl<'a> Parser<'a> { if self.eat_self() { if reference && !mutable { - // TODO: error + // TODO: error (found `&` but expected `&mut`) } Some(PatternOrSelf::SelfPattern(SelfPattern { reference, mutable })) } else { if reference { - // TODO: error + // TODO: error? (reference pattern but not `self`) } Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(mutable, start_span)?)) @@ -90,7 +90,7 @@ impl<'a> Parser<'a> { } if !path.is_ident() { - // TODO: error + // TODO: error (found something like foo::bar::baz for a pattern) let ident = path.segments.pop().unwrap().ident; return Some(Pattern::Identifier(ident)); } diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 6808fa39a51..cd2673d5185 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -158,7 +158,7 @@ impl<'a> Parser<'a> { if let Pattern::Identifier(ident) = param.pattern { Some((ident, param.typ)) } else { - // TODO: error + // TODO: error (expected a pattern identifier) None } }) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 349b572c6f1..ae1d48f929b 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -117,8 +117,11 @@ impl<'a> Parser<'a> { if let Some(int_type) = self.eat_int_type() { return Some(match UnresolvedTypeData::from_int_token(int_type) { Ok(typ) => typ, - Err(_) => { - // TODO: error + Err(err) => { + self.push_error( + ParserErrorReason::InvalidBitSize(err.0), + self.previous_token_span, + ); UnresolvedTypeData::Error } }); diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 2670b60cb72..fed57152b9e 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -84,7 +84,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path) -> UseTree { if prefix.segments.is_empty() { - // TODO: error + // TODO: error (`use (empty path)`); UseTree { prefix, kind: UseTreeKind::Path(Ident::default(), None) } } else { let ident = prefix.segments.pop().unwrap().ident; diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 8f93b9bac54..6d68ecdbfeb 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -36,7 +36,7 @@ impl<'a> Parser<'a> { } if constraints.is_empty() { - // TODO: error + // TODO: error? (`where` but no constrains) } constraints From ba742364975109da8dc4afe7093dd3ce990f90dd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 17:37:16 -0300 Subject: [PATCH 166/229] Clippy --- compiler/noirc_frontend/src/parser/parser.rs | 10 +++++----- compiler/noirc_frontend/src/parser/parser/impls.rs | 12 +++++------- compiler/noirc_frontend/src/parser/parser/infix.rs | 14 ++++++-------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index be62443f36c..f208b54aa85 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -418,13 +418,13 @@ impl<'a> Parser<'a> { fn eat_keyword_or_error(&mut self, keyword: Keyword) { if !self.eat_keyword(keyword) { - self.expected_token(Token::Keyword(keyword)) + self.expected_token(Token::Keyword(keyword)); } } fn eat_or_error(&mut self, token: Token) { if !self.eat(token.clone()) { - self.expected_token(token) + self.expected_token(token); } } @@ -454,7 +454,7 @@ impl<'a> Parser<'a> { token, self.token.token().clone(), self.current_token_span, - )) + )); } fn expected_one_of_tokens(&mut self, tokens: &[Token]) { @@ -462,7 +462,7 @@ impl<'a> Parser<'a> { tokens, self.token.token().clone(), self.current_token_span, - )) + )); } fn expected_label(&mut self, label: ParsingRuleLabel) { @@ -470,7 +470,7 @@ impl<'a> Parser<'a> { label, self.token.token().clone(), self.current_token_span, - )) + )); } fn expected_token_separating_items(&mut self, token: &str, items: &str, span: Span) { diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 100c18e2fcb..187e1385e5c 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -136,15 +136,13 @@ impl<'a> Parser<'a> { if self.eat_right_brace() { break; } + } else if self.is_eof() || self.eat_right_brace() { + break; } else { - if self.is_eof() || self.eat_right_brace() { - break; - } else { - self.expected_label(ParsingRuleLabel::TraitImplItem); + self.expected_label(ParsingRuleLabel::TraitImplItem); - // Keep going - self.next_token(); - } + // Keep going + self.next_token(); } } diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 9f40ba269e4..07794c8a637 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -129,9 +129,8 @@ impl<'a> Parser<'a> { BinaryOpKind::Less } else if self.eat(Token::LessEqual) { BinaryOpKind::LessEqual - } else - // Make sure to skip the `>>=` case, as `>>=` is lexed as `> >=`. - if self.next_token.token() != &Token::GreaterEqual && self.eat(Token::Greater) { + } else if self.next_token.token() != &Token::GreaterEqual && self.eat(Token::Greater) { + // Make sure to skip the `>>=` case, as `>>=` is lexed as `> >=`. BinaryOpKind::Greater } else if self.eat(Token::GreaterEqual) { BinaryOpKind::GreaterEqual @@ -159,13 +158,12 @@ impl<'a> Parser<'a> { let operator = if self.next_token.token() != &Token::Assign && self.eat(Token::ShiftLeft) { BinaryOpKind::ShiftLeft - } else - // 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. - if self.token.token() == &Token::Greater + } else if self.token.token() == &Token::Greater && self.next_token.token() == &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. self.next_token(); self.next_token(); BinaryOpKind::ShiftRight From 557bc2784fd809845cfadbfb48b8b8623ccb707f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 18:53:14 -0300 Subject: [PATCH 167/229] Fix parser test --- compiler/noirc_frontend/src/parser/parser/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 187e1385e5c..257625036cd 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -449,7 +449,7 @@ mod tests { fn parse_empty_impl_incorrect_body() { let src = "impl Foo { hello fn foo() {} }"; let (module, errors) = parse_program(src); - assert_eq!(errors.len(), 0); // TODO: this should be 1 + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Impl(type_impl) = &item.kind else { From 552da43eeecbcb5640c1522a5873bf31789e9bc3 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 19:02:53 -0300 Subject: [PATCH 168/229] Fix formatting of `self: Self` --- compiler/noirc_frontend/src/parser/parser/function.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 6652bcf0043..5377c5f3887 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -189,8 +189,11 @@ impl<'a> Parser<'a> { let ident = Ident::new("self".to_string(), span); let path = Path::from_single("Self".to_owned(), span); let no_args = GenericTypeArgs::default(); + let mut self_type_span = span; let mut self_type = if self.eat_colon() { - self.parse_type_or_error() + let typ = self.parse_type_or_error(); + self_type_span = typ.span; + typ } else { UnresolvedTypeData::Named(path, no_args, true).with_span(span) }; @@ -198,7 +201,7 @@ impl<'a> Parser<'a> { if self_pattern.reference { self_type = UnresolvedTypeData::MutableReference(Box::new(self_type)) - .with_span(span); + .with_span(self_type_span); } else if self_pattern.mutable { pattern = Pattern::Mutable(Box::new(pattern), span, true); } From f6eeafa8d05624ac9f0e138577c75635c9d90b63 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 19:06:39 -0300 Subject: [PATCH 169/229] clippy --- .../src/parser/parser/expression.rs | 14 ++++++------ .../src/parser/parser/generics.rs | 2 +- .../noirc_frontend/src/parser/parser/impls.rs | 2 +- .../src/parser/parser/statement.rs | 22 +++++++++---------- .../src/parser/parser/structs.rs | 2 +- .../noirc_frontend/src/parser/parser/tests.rs | 7 ++---- .../noirc_frontend/src/parser/parser/types.rs | 2 +- 7 files changed, 24 insertions(+), 27 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 0030567fb62..2c64ecbf71d 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1030,7 +1030,7 @@ mod tests { #[test] fn parses_variable_path_with_turbofish() { let src = "foo::<9>"; - let mut parser = Parser::for_str(&src); + 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!(expr.span.end() as usize, src.len()); @@ -1477,7 +1477,7 @@ mod tests { #[test] fn parses_comptime_expression() { let src = "comptime { 1 }"; - let mut parser = Parser::for_str(&src); + 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()); @@ -1490,7 +1490,7 @@ mod tests { #[test] fn parses_type_path() { let src = "Field::foo"; - let mut parser = Parser::for_str(&src); + 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()); @@ -1505,7 +1505,7 @@ mod tests { #[test] fn parses_type_path_with_generics() { let src = "Field::foo::"; - let mut parser = Parser::for_str(&src); + 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()); @@ -1520,7 +1520,7 @@ mod tests { #[test] fn parses_unquote_var() { let src = "$foo::bar"; - let mut parser = Parser::for_str(&src); + 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()); @@ -1536,13 +1536,13 @@ mod tests { #[test] fn parses_unquote_expr() { let src = "$(1 + 2)"; - let mut parser = Parser::for_str(&src); + 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()); let ExpressionKind::Unquote(expr) = expr.kind else { panic!("Expected unquote"); }; - assert_eq!(expr.kind.to_string(), "((1 + 2))") + assert_eq!(expr.kind.to_string(), "((1 + 2))"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 223060c3172..2ce2018d65d 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -204,7 +204,7 @@ mod tests { assert_eq!( typ.typ, UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) - ) + ); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 257625036cd..ccf90ac5a88 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -456,7 +456,7 @@ mod tests { panic!("Expected type impl"); }; assert_eq!(type_impl.object_type.to_string(), "Foo"); - assert_eq!(type_impl.methods.len(), 1) + assert_eq!(type_impl.methods.len(), 1); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index f978ce3ddf6..5f477834fb0 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -460,7 +460,7 @@ mod tests { #[test] fn parses_comptime_block() { let src = "comptime { 1 }"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Comptime(statement) = statement.kind else { @@ -478,7 +478,7 @@ mod tests { #[test] fn parses_comptime_let() { let src = "comptime let x = 1;"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Comptime(statement) = statement.kind else { @@ -492,7 +492,7 @@ mod tests { #[test] fn parses_for_array() { let src = "for i in x { }"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::For(for_loop) = statement.kind else { @@ -505,7 +505,7 @@ mod tests { #[test] fn parses_for_range() { let src = "for i in 0..10 { }"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::For(for_loop) = statement.kind else { @@ -518,7 +518,7 @@ mod tests { #[test] fn parses_comptime_for() { let src = "comptime for i in x { }"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Comptime(statement) = statement.kind else { @@ -534,7 +534,7 @@ mod tests { #[test] fn parses_assignment() { let src = "x = 1"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Assign(assign) = statement.kind else { @@ -550,7 +550,7 @@ mod tests { #[test] fn parses_assignment_with_parentheses() { let src = "(x)[0] = 1"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Assign(..) = statement.kind else { @@ -561,7 +561,7 @@ mod tests { #[test] fn parses_op_assignment() { let src = "x += 1"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Assign(assign) = statement.kind else { @@ -573,7 +573,7 @@ mod tests { #[test] fn parses_op_assignment_with_shift_right() { let src = "x >>= 1"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Assign(assign) = statement.kind else { @@ -586,7 +586,7 @@ mod tests { fn parses_if_statement_followed_by_tuple() { // This shouldn't be parsed as a call let src = "{ if 1 { 2 } (3, 4) }"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Expression(expr) = statement.kind else { @@ -602,7 +602,7 @@ mod tests { fn parses_block_followed_by_tuple() { // This shouldn't be parsed as a call let src = "{ { 2 } (3, 4) }"; - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); assert!(parser.errors.is_empty()); let StatementKind::Expression(expr) = statement.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index f9d2e515564..b60628dfa96 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -165,7 +165,7 @@ mod tests { assert_eq!( typ.typ, UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) - ) + ); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/tests.rs b/compiler/noirc_frontend/src/parser/parser/tests.rs index d9551b914a7..3901453d1c2 100644 --- a/compiler/noirc_frontend/src/parser/parser/tests.rs +++ b/compiler/noirc_frontend/src/parser/parser/tests.rs @@ -20,11 +20,8 @@ pub(super) fn get_source_with_error_span(src: &str) -> (String, Span) { (src, span) } -pub(super) fn get_single_error<'a>( - errors: &[ParserError], - expected_span: Span, -) -> &ParserErrorReason { +pub(super) fn get_single_error(errors: &[ParserError], expected_span: Span) -> &ParserErrorReason { assert_eq!(errors.len(), 1); assert_eq!(errors[0].span(), expected_span); - &errors[0].reason().unwrap() + errors[0].reason().unwrap() } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index ae1d48f929b..a5340cc081c 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -532,7 +532,7 @@ mod tests { let typ = parser.parse_type_or_error(); assert!(parser.errors.is_empty()); let UnresolvedTypeData::Quoted(parsed_qouted_type) = typ.typ else { - panic!("Expected a quoted type for {}", quoted_type.to_string()) + panic!("Expected a quoted type for {}", quoted_type) }; assert_eq!(parsed_qouted_type, quoted_type); } From 9ab6d2a3e9a23ea4044e47756cf61311de2d3f3d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 19:33:33 -0300 Subject: [PATCH 170/229] Fix the formatter bug for real --- .../src/parser/parser/function.rs | 19 ++++++++----------- tooling/nargo_fmt/tests/expected/impl.nr | 2 ++ tooling/nargo_fmt/tests/input/impl.nr | 2 ++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 5377c5f3887..147a23e97c7 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -185,32 +185,29 @@ impl<'a> Parser<'a> { } } PatternOrSelf::SelfPattern(self_pattern) => { - let span = self.previous_token_span; - let ident = Ident::new("self".to_string(), span); - let path = Path::from_single("Self".to_owned(), span); + 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_span = span; let mut self_type = if self.eat_colon() { - let typ = self.parse_type_or_error(); - self_type_span = typ.span; - typ + self.parse_type_or_error() } else { - UnresolvedTypeData::Named(path, no_args, true).with_span(span) + 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(self_type_span); + .with_span(ident_span); } else if self_pattern.mutable { - pattern = Pattern::Mutable(Box::new(pattern), span, true); + pattern = Pattern::Mutable(Box::new(pattern), ident_span, true); } parameters.push(Param { visibility: Visibility::Private, pattern, typ: self_type, - span, + span: self.span_since(ident_span), }); } } diff --git a/tooling/nargo_fmt/tests/expected/impl.nr b/tooling/nargo_fmt/tests/expected/impl.nr index 3c2fa42837a..2e7b8330eef 100644 --- a/tooling/nargo_fmt/tests/expected/impl.nr +++ b/tooling/nargo_fmt/tests/expected/impl.nr @@ -10,6 +10,8 @@ impl MyType { fn method(mut self) {} fn method(&mut self) {} + + fn method(self: Self) {} } impl MyType { diff --git a/tooling/nargo_fmt/tests/input/impl.nr b/tooling/nargo_fmt/tests/input/impl.nr index e4adb8ebd6a..5a04ac283b4 100644 --- a/tooling/nargo_fmt/tests/input/impl.nr +++ b/tooling/nargo_fmt/tests/input/impl.nr @@ -10,6 +10,8 @@ impl MyType { fn method(mut self) {} fn method(&mut self) {} + + fn method(self: Self) {} } impl MyType { From f2d3e0f5b43dad21b57337c75d247c1d5e3c9ba8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 30 Sep 2024 21:21:31 -0300 Subject: [PATCH 171/229] Correct way to detect SelfPattern --- .../src/parser/parser/function.rs | 7 +-- .../src/parser/parser/pattern.rs | 50 +++++++++++++++---- tooling/nargo_fmt/tests/expected/impl.nr | 4 ++ tooling/nargo_fmt/tests/input/impl.nr | 4 ++ 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 147a23e97c7..23ec28a1e6f 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -189,11 +189,8 @@ impl<'a> Parser<'a> { 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 = if self.eat_colon() { - self.parse_type_or_error() - } else { - UnresolvedTypeData::Named(path, no_args, true).with_span(ident_span) - }; + let mut self_type = + UnresolvedTypeData::Named(path, no_args, true).with_span(ident_span); let mut pattern = Pattern::Identifier(ident); if self_pattern.reference { diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index daf60d68006..ef9bd098280 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -13,6 +13,7 @@ pub(crate) enum PatternOrSelf { 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, @@ -37,22 +38,49 @@ impl<'a> Parser<'a> { pub(crate) fn parse_pattern_or_self(&mut self) -> Option { let start_span = self.current_token_span; - let reference = self.eat(Token::Ampersand); - let mutable = self.eat_keyword(Keyword::Mut); + if !self.next_is_colon() && self.eat_self() { + return Some(PatternOrSelf::SelfPattern(SelfPattern { + reference: false, + mutable: false, + })); + } - if self.eat_self() { - if reference && !mutable { - // TODO: error (found `&` but expected `&mut`) + 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)?, + )); } + } - Some(PatternOrSelf::SelfPattern(SelfPattern { reference, mutable })) - } else { - if reference { - // TODO: error? (reference pattern but not `self`) + if self.token.token() == &Token::Ampersand + && self.next_token.token() == &Token::Keyword(Keyword::Mut) + { + self.next_token(); + self.next_token(); + if !self.next_is_colon() && self.eat_self() { + return Some(PatternOrSelf::SelfPattern(SelfPattern { + reference: true, + mutable: true, + })); + } else { + // TODO: error (found `&mut` but `self` doesn't follow) + return Some(PatternOrSelf::Pattern( + self.parse_pattern_after_modifiers(true, start_span)?, + )); } - - Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(mutable, start_span)?)) } + + Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_span)?)) + } + + fn next_is_colon(&self) -> bool { + self.next_token.token() == &Token::Colon } pub(crate) fn parse_pattern_after_modifiers( diff --git a/tooling/nargo_fmt/tests/expected/impl.nr b/tooling/nargo_fmt/tests/expected/impl.nr index 2e7b8330eef..84394f6fa1d 100644 --- a/tooling/nargo_fmt/tests/expected/impl.nr +++ b/tooling/nargo_fmt/tests/expected/impl.nr @@ -12,6 +12,10 @@ impl MyType { 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/impl.nr b/tooling/nargo_fmt/tests/input/impl.nr index 5a04ac283b4..53c9759ca1b 100644 --- a/tooling/nargo_fmt/tests/input/impl.nr +++ b/tooling/nargo_fmt/tests/input/impl.nr @@ -12,6 +12,10 @@ impl MyType { fn method(&mut self) {} fn method(self: Self) {} + + fn method(mut self: Self) {} + + fn method(&mut self: Self) {} } impl MyType { From 154484884b41e531ed263779203ad2a009948233 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 09:59:22 -0300 Subject: [PATCH 172/229] Take care of the remaining error cases --- compiler/noirc_frontend/src/parser/errors.rs | 8 ++ compiler/noirc_frontend/src/parser/labels.rs | 2 + .../src/parser/parser/attributes.rs | 6 +- .../src/parser/parser/doc_comments.rs | 6 +- .../src/parser/parser/expression.rs | 111 +++++++++--------- .../src/parser/parser/function.rs | 18 +-- .../src/parser/parser/generics.rs | 18 +-- .../src/parser/parser/global.rs | 10 +- .../noirc_frontend/src/parser/parser/impls.rs | 33 +++--- .../src/parser/parser/item_visibility.rs | 8 +- .../src/parser/parser/module.rs | 11 +- .../noirc_frontend/src/parser/parser/path.rs | 18 +-- .../src/parser/parser/pattern.rs | 25 ++-- .../src/parser/parser/statement.rs | 36 +++--- .../src/parser/parser/structs.rs | 21 ++-- .../noirc_frontend/src/parser/parser/tests.rs | 11 ++ .../src/parser/parser/traits.rs | 23 ++-- .../src/parser/parser/type_alias.rs | 9 +- .../src/parser/parser/type_expression.rs | 32 ++--- .../noirc_frontend/src/parser/parser/types.rs | 46 ++++---- .../src/parser/parser/use_tree.rs | 28 +++-- .../src/parser/parser/where_clause.rs | 12 +- 22 files changed, 274 insertions(+), 218 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index ffa6759acb0..47d5cec97a6 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -35,6 +35,14 @@ pub enum ParserErrorReason { 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, diff --git a/compiler/noirc_frontend/src/parser/labels.rs b/compiler/noirc_frontend/src/parser/labels.rs index 65719294c2e..4d23ef103fa 100644 --- a/compiler/noirc_frontend/src/parser/labels.rs +++ b/compiler/noirc_frontend/src/parser/labels.rs @@ -28,6 +28,7 @@ pub enum ParsingRuleLabel { TypeExpression, TypeOrTypeExpression, TokenKind(TokenKind), + UseSegment, } impl fmt::Display for ParsingRuleLabel { @@ -55,6 +56,7 @@ impl fmt::Display for ParsingRuleLabel { ParsingRuleLabel::TypeExpression => write!(f, "type expression"), 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/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 4008eaa6d25..8312a6446c8 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -50,7 +50,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - parser::Parser, + parser::{parser::tests::expect_no_errors, Parser}, token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, }; @@ -61,7 +61,7 @@ mod tests { let Some(SecondaryAttribute::Custom(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner custom attribute"); }; - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(custom.contents, "hello"); } @@ -70,7 +70,7 @@ mod tests { let src = "#[test] #[deprecated]"; let mut parser = Parser::for_str(src); let mut attributes = parser.parse_attributes(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(attributes.len(), 2); let (attr, _) = attributes.remove(0); diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 0142e00d0a6..f8592cbe111 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -38,14 +38,14 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::Parser; + 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(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); assert_eq!(comments[0], " Hello"); assert_eq!(comments[1], " World"); @@ -56,7 +56,7 @@ mod tests { let src = "/// Hello\n/// World"; let mut parser = Parser::for_str(src); let comments = parser.parse_outer_doc_comments(); - assert!(parser.errors.is_empty()); + 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 index 2c64ecbf71d..a172808e412 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -118,7 +118,7 @@ impl<'a> Parser<'a> { ParserErrorReason::AssociatedTypesNotAllowedInMethodCalls, ); if generics.is_none() { - // TODO: error (found `::` but not `::<...>`) + self.expected_token(Token::Less); } generics } else { @@ -430,7 +430,10 @@ impl<'a> Parser<'a> { return Some(ExpressionKind::Unquote(Box::new(expr))); } - // TODO: error (found $ but nothing to unquote) + self.push_error( + ParserErrorReason::ExpectedIdentifierOrLeftParenAfterDollar, + self.current_token_span, + ); None } @@ -454,7 +457,7 @@ impl<'a> Parser<'a> { let turbofish = if self.eat_double_colon() { let generics = self.parse_generic_type_args(); if generics.is_empty() { - // TODO: error? (found `::` but not `::<...>`) + self.expected_token(Token::Less); } generics } else { @@ -669,7 +672,7 @@ mod tests { UnresolvedTypeData, }, parser::{ - parser::tests::{get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -680,7 +683,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Bool(true)))); let src = "false"; @@ -694,7 +697,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); }; @@ -708,7 +711,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); }; @@ -722,7 +725,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); }; @@ -739,7 +742,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Unit))); } @@ -749,7 +752,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Str(string)) = expr.kind else { panic!("Expected string literal"); }; @@ -762,7 +765,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::RawStr(string, n)) = expr.kind else { panic!("Expected raw string literal"); }; @@ -776,7 +779,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::FmtStr(string)) = expr.kind else { panic!("Expected format string literal"); }; @@ -789,7 +792,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Tuple(mut exprs) = expr.kind else { panic!("Expected tuple expression"); }; @@ -816,7 +819,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Block(mut block) = expr.kind else { panic!("Expected block expression"); }; @@ -845,7 +848,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Block(block) = expr.kind else { panic!("Expected block expression"); }; @@ -887,7 +890,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Unsafe(block, _) = expr.kind else { panic!("Expected unsafe expression"); }; @@ -930,7 +933,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected array literal"); @@ -944,7 +947,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected array literal"); @@ -959,7 +962,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected array literal"); @@ -975,7 +978,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { repeated_element, length, @@ -993,7 +996,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected slice literal"); @@ -1007,7 +1010,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); }; @@ -1020,7 +1023,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); }; @@ -1034,7 +1037,7 @@ mod tests { let expr = parser.parse_expression_or_error(); assert_eq!(expr.span.end() as usize, src.len()); assert_eq!(expr.span.end() as usize, src.len()); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); } #[test] @@ -1043,7 +1046,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1061,7 +1064,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1079,7 +1082,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1097,7 +1100,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1115,7 +1118,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Quote(tokens) = expr.kind else { panic!("Expected quote expression"); }; @@ -1128,7 +1131,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); }; @@ -1143,7 +1146,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); }; @@ -1158,7 +1161,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); }; @@ -1173,7 +1176,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::MemberAccess(member_access) = expr.kind else { panic!("Expected member access expression"); }; @@ -1187,7 +1190,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); }; @@ -1204,7 +1207,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); }; @@ -1221,7 +1224,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); }; @@ -1238,7 +1241,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Constructor(constructor) = expr.kind else { panic!("Expected constructor"); }; @@ -1252,7 +1255,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Constructor(mut constructor) = expr.kind else { panic!("Expected constructor"); }; @@ -1278,7 +1281,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1297,7 +1300,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1310,7 +1313,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1324,7 +1327,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1340,7 +1343,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Cast(cast_expr) = expr.kind else { panic!("Expected cast"); }; @@ -1367,7 +1370,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Index(index_expr) = expr.kind else { panic!("Expected index"); }; @@ -1398,7 +1401,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Infix(infix_expr) = expr.kind else { panic!("Expected infix"); }; @@ -1413,7 +1416,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Lambda(lambda) = expr.kind else { panic!("Expected lambda"); }; @@ -1428,7 +1431,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Lambda(mut lambda) = expr.kind else { panic!("Expected lambda"); }; @@ -1449,7 +1452,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Lambda(lambda) = expr.kind else { panic!("Expected lambda"); }; @@ -1464,7 +1467,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::AsTraitPath(as_trait_path) = expr.kind else { panic!("Expected as_trait_path") }; @@ -1480,7 +1483,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Comptime(block, _) = expr.kind else { panic!("Expected comptime block"); }; @@ -1493,7 +1496,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::TypePath(type_path) = expr.kind else { panic!("Expected type_path"); }; @@ -1508,7 +1511,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::TypePath(type_path) = expr.kind else { panic!("Expected type_path"); }; @@ -1523,7 +1526,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Unquote(expr) = expr.kind else { panic!("Expected unquote"); }; @@ -1539,7 +1542,7 @@ mod tests { 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()); + expect_no_errors(&parser.errors); let ExpressionKind::Unquote(expr) = expr.kind else { panic!("Expected unquote"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 23ec28a1e6f..290cf95195f 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -294,7 +294,7 @@ mod tests { parser::{ parser::{ parse_program, - tests::{get_single_error, get_source_with_error_span}, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, ItemKind, ParserErrorReason, }, @@ -304,7 +304,7 @@ mod tests { fn parse_simple_function() { let src = "fn foo() {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Function(noir_function) = &item.kind else { @@ -319,7 +319,7 @@ mod tests { fn parse_function_with_generics() { let src = "fn foo() {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Function(noir_function) = &item.kind else { @@ -332,7 +332,7 @@ mod tests { fn parse_function_with_arguments() { let src = "fn foo(x: Field, y: Field) {}"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Function(mut noir_function) = item.kind else { @@ -355,7 +355,7 @@ mod tests { fn parse_function_with_argument_pub_visibility() { let src = "fn foo(x: pub Field) {}"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Function(mut noir_function) = item.kind else { @@ -373,7 +373,7 @@ mod tests { fn parse_function_with_argument_return_data_visibility() { let src = "fn foo(x: return_data Field) {}"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Function(mut noir_function) = item.kind else { @@ -389,7 +389,7 @@ mod tests { fn parse_function_with_argument_call_data_visibility() { let src = "fn foo(x: call_data(42) Field) {}"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Function(mut noir_function) = item.kind else { @@ -405,7 +405,7 @@ mod tests { fn parse_function_return_type() { let src = "fn foo() -> Field {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Function(noir_function) = &item.kind else { @@ -419,7 +419,7 @@ mod tests { fn parse_function_return_visibility() { let src = "fn foo() -> pub Field {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Function(noir_function) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 2ce2018d65d..9413ddc0074 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -5,7 +5,7 @@ use crate::{ GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, }, - parser::ParserErrorReason, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Keyword, Token, TokenKind}, }; @@ -139,7 +139,9 @@ impl<'a> Parser<'a> { } else { let typ = self.parse_type_or_type_expression(); let Some(typ) = typ else { - // TODO: error? (not sure if this is `<>` so test that) + if generic_type_args.is_empty() { + self.expected_label(ParsingRuleLabel::TypeOrTypeExpression); + } self.eat_greater(); break; }; @@ -170,7 +172,7 @@ mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ - parser::tests::{get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -187,7 +189,7 @@ mod tests { let src = ""; let mut parser = Parser::for_str(src); let mut generics = parser.parse_generics(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(generics.len(), 2); let generic = generics.remove(0); @@ -212,7 +214,7 @@ mod tests { let src = "1"; let mut parser = Parser::for_str(src); let generics = parser.parse_generic_type_args(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(generics.is_empty()); } @@ -221,7 +223,7 @@ mod tests { let src = ""; let mut parser = Parser::for_str(src); let generics = parser.parse_generic_type_args(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(!generics.is_empty()); assert_eq!(generics.ordered_args.len(), 1); assert_eq!(generics.ordered_args[0].to_string(), "i32"); @@ -235,7 +237,7 @@ mod tests { let src = ""; let mut parser = Parser::for_str(src); let generics = parser.parse_generic_type_args(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(!generics.is_empty()); assert_eq!(generics.ordered_args.len(), 1); assert_eq!(generics.ordered_args[0].to_string(), "foo::Bar"); @@ -247,7 +249,7 @@ mod tests { let src = "<1>"; let mut parser = Parser::for_str(src); let generics = parser.parse_generic_type_args(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(!generics.is_empty()); assert_eq!(generics.ordered_args.len(), 1); assert_eq!(generics.ordered_args[0].to_string(), "1"); diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index fa19a63e695..8e8e1a1c537 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -74,7 +74,7 @@ mod tests { parser::{ parser::{ parse_program, - tests::{get_single_error, get_source_with_error_span}, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, ItemKind, ParserErrorReason, }, @@ -84,7 +84,7 @@ mod tests { fn parse_global_no_type_annotation() { let src = "global foo = 1;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Global(let_statement, visibility) = &item.kind else { @@ -103,7 +103,7 @@ mod tests { fn parse_global_with_type_annotation() { let src = "global foo: i32 = 1;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Global(let_statement, _) = &item.kind else { @@ -123,7 +123,7 @@ mod tests { fn parse_comptime_global() { let src = "comptime global foo: i32 = 1;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Global(let_statement, _) = &item.kind else { @@ -136,7 +136,7 @@ mod tests { fn parse_mutable_global() { let src = "mut global foo: i32 = 1;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Global(let_statement, _) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index ccf90ac5a88..22e4762a51e 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -249,14 +249,17 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ItemVisibility, Pattern, TraitImplItemKind, UnresolvedTypeData}, - parser::{parser::parse_program, ItemKind}, + parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }, }; #[test] fn parse_empty_impl() { let src = "impl Foo {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Impl(type_impl) = &item.kind else { @@ -271,7 +274,7 @@ mod tests { fn parse_empty_impl_with_generics() { let src = "impl Foo {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Impl(type_impl) = &item.kind else { @@ -286,7 +289,7 @@ mod tests { fn parse_impl_with_methods() { let src = "impl Foo { unconstrained fn foo() {} pub comptime fn bar() {} }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Impl(mut type_impl) = item.kind else { @@ -318,7 +321,7 @@ mod tests { } "; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Impl(type_impl) = item.kind else { @@ -332,7 +335,7 @@ mod tests { fn parse_impl_with_self_argument() { let src = "impl Foo { fn foo(self) {} }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Impl(mut type_impl) = item.kind else { @@ -357,7 +360,7 @@ mod tests { fn parse_impl_with_mut_self_argument() { let src = "impl Foo { fn foo(mut self) {} }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Impl(mut type_impl) = item.kind else { @@ -386,7 +389,7 @@ mod tests { fn parse_impl_with_reference_mut_self_argument() { let src = "impl Foo { fn foo(&mut self) {} }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Impl(mut type_impl) = item.kind else { @@ -411,7 +414,7 @@ mod tests { fn parse_impl_with_self_argument_followed_by_type() { let src = "impl Foo { fn foo(self: Foo) {} }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Impl(mut type_impl) = item.kind else { @@ -463,7 +466,7 @@ mod tests { fn parse_empty_trait_impl() { let src = "impl Foo for Field {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::TraitImpl(trait_impl) = &item.kind else { @@ -479,7 +482,7 @@ mod tests { fn parse_empty_trait_impl_with_generics() { let src = "impl Foo for Field {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::TraitImpl(trait_impl) = &item.kind else { @@ -495,7 +498,7 @@ mod tests { fn parse_trait_impl_with_function() { let src = "impl Foo for Field { fn foo() {} }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::TraitImpl(mut trait_impl) = item.kind else { @@ -516,7 +519,7 @@ mod tests { fn parse_trait_impl_with_generic_type_args() { let src = "impl Foo for Field { }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::TraitImpl(trait_impl) = item.kind else { @@ -530,7 +533,7 @@ mod tests { fn parse_trait_impl_with_type() { let src = "impl Foo for Field { type Foo = i32; }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::TraitImpl(mut trait_impl) = item.kind else { @@ -551,7 +554,7 @@ mod tests { fn parse_trait_impl_with_let() { let src = "impl Foo for Field { let x: Field = 1; }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::TraitImpl(mut trait_impl) = item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 0670f2f9a81..f4214e65dd1 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -38,7 +38,7 @@ mod tests { use crate::{ ast::ItemVisibility, parser::{ - parser::tests::{get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -48,7 +48,7 @@ mod tests { let src = "("; let mut parser = Parser::for_str(src); let visibility = parser.parse_item_visibility(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Private); } @@ -57,7 +57,7 @@ mod tests { let src = "pub"; let mut parser = Parser::for_str(src); let visibility = parser.parse_item_visibility(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Public); } @@ -107,7 +107,7 @@ mod tests { let src = "pub(crate)"; let mut parser = Parser::for_str(src); let visibility = parser.parse_item_visibility(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::PublicCrate); } } diff --git a/compiler/noirc_frontend/src/parser/parser/module.rs b/compiler/noirc_frontend/src/parser/parser/module.rs index 56ab85bd746..a1bf24dd22c 100644 --- a/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -49,14 +49,17 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::{parser::parse_program, ItemKind}; + 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); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::ModuleDecl(module) = &item.kind else { @@ -70,7 +73,7 @@ mod tests { let src = "mod foo { mod bar; }"; let (module, errors) = parse_program(src); dbg!(&errors); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Submodules(parsed_submodule) = &item.kind else { @@ -86,7 +89,7 @@ mod tests { let src = "contract foo {}"; let (module, errors) = parse_program(src); dbg!(&errors); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Submodules(parsed_submodule) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index ca5581b3d24..116b3a8fd23 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -169,14 +169,17 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::{ast::PathKind, parser::Parser}; + use crate::{ + ast::PathKind, + parser::{parser::tests::expect_no_errors, Parser}, + }; #[test] fn parses_plain_one_segment() { let src = "foo"; let mut parser = Parser::for_str(src); let path = parser.parse_path_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 1); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -188,7 +191,7 @@ mod tests { let src = "foo::bar"; let mut parser = Parser::for_str(src); let path = parser.parse_path_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -202,8 +205,7 @@ mod tests { let src = "crate::foo::bar"; let mut parser = Parser::for_str(src); let path = parser.parse_path_or_error(); - dbg!(path.to_string()); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(path.kind, PathKind::Crate); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -217,7 +219,7 @@ mod tests { let src = "super::foo::bar"; let mut parser = Parser::for_str(src); let path = parser.parse_path_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(path.kind, PathKind::Super); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -231,7 +233,7 @@ mod tests { let src = "dep::foo::bar"; let mut parser = Parser::for_str(src); let path = parser.parse_path_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(path.kind, PathKind::Dep); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); @@ -258,7 +260,7 @@ mod tests { let src = "foo::::bar"; let mut parser = Parser::for_str(src); let mut path = parser.parse_path_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 2); assert_eq!(path.segments[0].ident.to_string(), "foo"); diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index ef9bd098280..918927cd525 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -2,7 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, Path, Pattern}, - parser::labels::ParsingRuleLabel, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Keyword, Token, TokenKind}, }; @@ -69,7 +69,10 @@ impl<'a> Parser<'a> { mutable: true, })); } else { - // TODO: error (found `&mut` but `self` doesn't follow) + self.push_error( + ParserErrorReason::RefMutCanOnlyBeUsedWithSelf, + self.current_token_span, + ); return Some(PatternOrSelf::Pattern( self.parse_pattern_after_modifiers(true, start_span)?, )); @@ -118,7 +121,8 @@ impl<'a> Parser<'a> { } if !path.is_ident() { - // TODO: error (found something like foo::bar::baz for a pattern) + self.push_error(ParserErrorReason::InvalidPattern, path.span); + let ident = path.segments.pop().unwrap().ident; return Some(Pattern::Identifier(ident)); } @@ -212,14 +216,17 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::{ast::Pattern, parser::Parser}; + use crate::{ + ast::Pattern, + parser::{parser::tests::expect_no_errors, Parser}, + }; #[test] fn parses_identifier_pattern() { let src = "foo"; let mut parser = Parser::for_str(src); let pattern = parser.parse_pattern_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let Pattern::Identifier(ident) = pattern else { panic!("Expected an identifier pattern") }; assert_eq!(ident.to_string(), "foo"); } @@ -229,7 +236,7 @@ mod tests { let src = "mut foo"; let mut parser = Parser::for_str(src); let pattern = parser.parse_pattern_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); 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") }; @@ -241,7 +248,7 @@ mod tests { let src = "(foo, bar)"; let mut parser = Parser::for_str(src); let pattern = parser.parse_pattern_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let Pattern::Tuple(mut patterns, _) = pattern else { panic!("Expected a tuple pattern") }; assert_eq!(patterns.len(), 2); @@ -269,7 +276,7 @@ mod tests { let src = "foo::Bar {}"; let mut parser = Parser::for_str(src); let pattern = parser.parse_pattern_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let Pattern::Struct(path, patterns, _) = pattern else { panic!("Expected a struct pattern") }; @@ -282,7 +289,7 @@ mod tests { let src = "foo::Bar { x: one, y }"; let mut parser = Parser::for_str(src); let pattern = parser.parse_pattern_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let Pattern::Struct(path, mut patterns, _) = pattern else { panic!("Expected a struct pattern") }; diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 5f477834fb0..09dd2e65528 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -359,7 +359,7 @@ mod tests { use crate::{ ast::{ConstrainKind, ExpressionKind, ForRange, LValue, StatementKind, UnresolvedTypeData}, parser::{ - parser::tests::{get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -369,7 +369,7 @@ mod tests { let src = "break"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(matches!(statement.kind, StatementKind::Break)); } @@ -378,7 +378,7 @@ mod tests { let src = "continue"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(matches!(statement.kind, StatementKind::Continue)); } @@ -387,7 +387,7 @@ mod tests { let src = "let x = 1;"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -402,7 +402,7 @@ mod tests { let src = "let x: Field = 1;"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -417,7 +417,7 @@ mod tests { let src = "assert(true, \"good\")"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -430,7 +430,7 @@ mod tests { let src = "assert_eq(1, 2, \"bad\")"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -462,7 +462,7 @@ mod tests { let src = "comptime { 1 }"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); }; @@ -480,7 +480,7 @@ mod tests { let src = "comptime let x = 1;"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); }; @@ -494,7 +494,7 @@ mod tests { let src = "for i in x { }"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -507,7 +507,7 @@ mod tests { let src = "for i in 0..10 { }"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -520,7 +520,7 @@ mod tests { let src = "comptime for i in x { }"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime"); }; @@ -536,7 +536,7 @@ mod tests { let src = "x = 1"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -552,7 +552,7 @@ mod tests { let src = "(x)[0] = 1"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Assign(..) = statement.kind else { panic!("Expected assign"); }; @@ -563,7 +563,7 @@ mod tests { let src = "x += 1"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -575,7 +575,7 @@ mod tests { let src = "x >>= 1"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -588,7 +588,7 @@ mod tests { let src = "{ if 1 { 2 } (3, 4) }"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Expression(expr) = statement.kind else { panic!("Expected expr"); }; @@ -604,7 +604,7 @@ mod tests { let src = "{ { 2 } (3, 4) }"; let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let StatementKind::Expression(expr) = statement.kind else { panic!("Expected expr"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index b60628dfa96..4d87397ff20 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -2,6 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Documented, Ident, ItemVisibility, NoirStruct, StructField, UnresolvedGenerics}, + parser::ParserErrorReason, token::{Attribute, SecondaryAttribute, Token}, }; @@ -42,11 +43,17 @@ impl<'a> Parser<'a> { let mut trailing_comma = false; loop { + let doc_comments_start_span = self.current_token_span; let doc_comments = self.parse_outer_doc_comments(); let start_span = self.current_token_span; let Some(name) = self.eat_ident() else { - // TODO: error if there are doc comments + if !doc_comments.is_empty() { + self.push_error( + ParserErrorReason::DocCommentDoesNotDocumentAnything, + self.span_since(doc_comments_start_span), + ); + } break; }; @@ -101,7 +108,7 @@ mod tests { parser::{ parser::{ parse_program, - tests::{get_single_error, get_source_with_error_span}, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, ItemKind, ParserErrorReason, }, @@ -111,7 +118,7 @@ mod tests { fn parse_empty_struct() { let src = "struct Foo {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Struct(noir_struct) = &item.kind else { @@ -126,7 +133,7 @@ mod tests { fn parse_empty_struct_followed_by_semicolon() { let src = "struct Foo;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Struct(noir_struct) = &item.kind else { @@ -141,7 +148,7 @@ mod tests { fn parse_empty_struct_with_generics() { let src = "struct Foo {}"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Struct(mut noir_struct) = item.kind else { @@ -172,7 +179,7 @@ mod tests { fn parse_struct_with_fields() { let src = "struct Foo { x: i32, y: Field }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Struct(mut noir_struct) = item.kind else { @@ -197,7 +204,7 @@ mod tests { fn parse_empty_struct_with_doc_comments() { let src = "/// Hello\nstruct Foo {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; assert_eq!(item.doc_comments.len(), 1); diff --git a/compiler/noirc_frontend/src/parser/parser/tests.rs b/compiler/noirc_frontend/src/parser/parser/tests.rs index 3901453d1c2..1de8f34ee57 100644 --- a/compiler/noirc_frontend/src/parser/parser/tests.rs +++ b/compiler/noirc_frontend/src/parser/parser/tests.rs @@ -25,3 +25,14 @@ pub(super) fn get_single_error(errors: &[ParserError], expected_span: Span) -> & assert_eq!(errors[0].span(), expected_span); errors[0].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 cd2673d5185..a38e6364f97 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -5,7 +5,7 @@ use crate::{ Documented, Ident, ItemVisibility, NoirTrait, Pattern, TraitItem, UnresolvedType, UnresolvedTypeData, }, - parser::labels::ParsingRuleLabel, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Attribute, Keyword, SecondaryAttribute, Token}, }; @@ -158,7 +158,7 @@ impl<'a> Parser<'a> { if let Pattern::Identifier(ident) = param.pattern { Some((ident, param.typ)) } else { - // TODO: error (expected a pattern identifier) + self.push_error(ParserErrorReason::InvalidPattern, param.pattern.span()); None } }) @@ -198,14 +198,17 @@ fn empty_trait( mod tests { use crate::{ ast::TraitItem, - parser::{parser::parse_program, ItemKind}, + parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }, }; #[test] fn parse_empty_trait() { let src = "trait Foo {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Trait(noir_trait) = &item.kind else { @@ -221,7 +224,7 @@ mod tests { fn parse_trait_with_generics() { let src = "trait Foo {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Trait(noir_trait) = &item.kind else { @@ -237,7 +240,7 @@ mod tests { fn parse_trait_with_where_clause() { let src = "trait Foo where A: Z {}"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Trait(noir_trait) = &item.kind else { @@ -253,7 +256,7 @@ mod tests { fn parse_trait_with_type() { let src = "trait Foo { type Elem; }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Trait(mut noir_trait) = item.kind else { @@ -272,7 +275,7 @@ mod tests { fn parse_trait_with_constant() { let src = "trait Foo { let x: Field = 1; }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Trait(mut noir_trait) = item.kind else { @@ -293,7 +296,7 @@ mod tests { fn parse_trait_with_function_no_body() { let src = "trait Foo { fn foo(); }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Trait(mut noir_trait) = item.kind else { @@ -312,7 +315,7 @@ mod tests { fn parse_trait_with_function_with_body() { let src = "trait Foo { fn foo() {} }"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); let ItemKind::Trait(mut noir_trait) = item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index ad9173cdfa7..d652a58d881 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -55,14 +55,17 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::UnresolvedTypeData, - parser::{parser::parse_program, ItemKind}, + parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }, }; #[test] fn parse_type_alias_no_generics() { let src = "type Foo = Field;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::TypeAlias(alias) = &item.kind else { @@ -77,7 +80,7 @@ mod tests { fn parse_type_alias_with_generics() { let src = "type Foo = Field;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::TypeAlias(alias) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index cd3847afdfd..2108dc31214 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -391,7 +391,7 @@ mod tests { use crate::{ ast::{UnresolvedTypeData, UnresolvedTypeExpression}, - parser::Parser, + parser::{parser::tests::expect_no_errors, Parser}, BinaryTypeOperator, }; @@ -400,7 +400,7 @@ mod tests { let src = "42"; let mut parser = Parser::for_str(src); let expr = parser.parse_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeExpression::Constant(n, _) = expr else { panic!("Expected constant"); }; @@ -412,7 +412,7 @@ mod tests { let src = "foo::bar"; let mut parser = Parser::for_str(src); let expr = parser.parse_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeExpression::Variable(path) = expr else { panic!("Expected path"); }; @@ -424,7 +424,7 @@ mod tests { let src = "1 + 2 * 3 + 4"; let mut parser = Parser::for_str(src); let expr = parser.parse_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeExpression::BinaryOperation(lhs, operator, rhs, _) = expr else { panic!("Expected binary operation"); }; @@ -438,7 +438,7 @@ mod tests { let src = "(N)"; let mut parser = Parser::for_str(src); let expr = parser.parse_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeExpression::Variable(path) = expr else { panic!("Expected variable"); }; @@ -450,7 +450,7 @@ mod tests { let src = "-N"; let mut parser = Parser::for_str(src); let expr = parser.parse_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(expr.to_string(), "(0 - N)"); } @@ -459,7 +459,7 @@ mod tests { let src = "42"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression"); }; @@ -474,7 +474,7 @@ mod tests { let src = "foo::Bar"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { panic!("Expected named type"); }; @@ -487,7 +487,7 @@ mod tests { let src = "1 + 2 * 3 + 4"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression"); }; @@ -504,7 +504,7 @@ mod tests { let src = "-N"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression"); }; @@ -516,7 +516,7 @@ mod tests { let src = "()"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Unit = typ.typ else { panic!("Expected unit type"); }; @@ -527,7 +527,7 @@ mod tests { let src = "(Field)"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected parenthesized type"); }; @@ -541,7 +541,7 @@ mod tests { let src = "(1)"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression type"); }; @@ -553,7 +553,7 @@ mod tests { let src = "(Field, bool)"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Tuple(types) = typ.typ else { panic!("Expected tuple type"); }; @@ -570,7 +570,7 @@ mod tests { let src = "(Field,)"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Tuple(types) = typ.typ else { panic!("Expected tuple type"); }; @@ -585,7 +585,7 @@ mod tests { let src = "N - 1"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_type_expression().unwrap(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression type"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index a5340cc081c..36036162f54 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -456,7 +456,7 @@ mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, parser::{ - parser::tests::{get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, ParserErrorReason, }, QuotedType, @@ -467,7 +467,7 @@ mod tests { let src = "()"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(matches!(typ.typ, UnresolvedTypeData::Unit)); } @@ -476,7 +476,7 @@ mod tests { let src = "bool"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); } @@ -485,7 +485,7 @@ mod tests { let src = "u32"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(matches!( typ.typ, UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) @@ -497,7 +497,7 @@ mod tests { let src = "Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } @@ -506,7 +506,7 @@ mod tests { let src = "str<10>"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::String(expr) = typ.typ else { panic!("Expected a string type") }; assert_eq!(expr.to_string(), "10"); } @@ -516,7 +516,7 @@ mod tests { let src = "fmtstr<10, T>"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::FormatString(expr, typ) = typ.typ else { panic!("Expected a format string type") }; @@ -530,7 +530,7 @@ mod tests { let src = quoted_type.to_string(); let mut parser = Parser::for_str(&src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Quoted(parsed_qouted_type) = typ.typ else { panic!("Expected a quoted type for {}", quoted_type) }; @@ -543,7 +543,7 @@ mod tests { let src = "(Field, bool)"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; assert_eq!(types.len(), 2); @@ -559,7 +559,7 @@ mod tests { let src = "(Field,)"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Tuple(mut types) = typ.typ else { panic!("Expected a tuple type") }; assert_eq!(types.len(), 1); @@ -572,7 +572,7 @@ mod tests { let src = "(Field)"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; @@ -584,7 +584,7 @@ mod tests { let src = "(Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); // TODO: there should be an error here + expect_no_errors(&parser.errors); // TODO: there should be an error here let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; @@ -596,7 +596,7 @@ mod tests { let src = "&mut Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::MutableReference(typ) = typ.typ else { panic!("Expected a mutable reference type") }; @@ -608,7 +608,7 @@ mod tests { let src = "foo::Bar"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { panic!("Expected a named type") }; @@ -621,7 +621,7 @@ mod tests { let src = "[Field]"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Slice(typ) = typ.typ else { panic!("Expected a slice type") }; assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } @@ -644,7 +644,7 @@ mod tests { let src = "[Field; 10]"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Array(expr, typ) = typ.typ else { panic!("Expected an array type") }; @@ -657,7 +657,7 @@ mod tests { let src = "fn() -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Function(args, ret, env, unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -672,7 +672,7 @@ mod tests { let src = "fn(Field, bool) -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Function(args, _ret, _env, _unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -686,7 +686,7 @@ mod tests { let src = "fn() -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Function(_args, ret, _env, _unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -698,7 +698,7 @@ mod tests { let src = "fn[Field]() -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Function(_args, _ret, env, _unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -710,7 +710,7 @@ mod tests { let src = "unconstrained fn() -> Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::Function(_args, _ret, _env, unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -722,7 +722,7 @@ mod tests { let src = "impl foo::Bar"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::TraitAsType(path, generics) = typ.typ else { panic!("Expected trait as type") }; @@ -735,7 +735,7 @@ mod tests { let src = "::baz"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); let UnresolvedTypeData::AsTraitPath(as_trait_path) = typ.typ else { panic!("Expected as_trait_path") }; diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index fed57152b9e..f21f4ff2765 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -2,6 +2,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, Path, PathKind, UseTree, UseTreeKind}, + parser::labels::ParsingRuleLabel, token::{Keyword, Token}, }; @@ -84,7 +85,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path) -> UseTree { if prefix.segments.is_empty() { - // TODO: error (`use (empty path)`); + self.expected_label(ParsingRuleLabel::UseSegment); UseTree { prefix, kind: UseTreeKind::Path(Ident::default(), None) } } else { let ident = prefix.segments.pop().unwrap().ident; @@ -106,14 +107,17 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ItemVisibility, PathKind, UseTreeKind}, - parser::{parser::parse_program, ItemKind}, + parser::{ + parser::{parse_program, tests::expect_no_errors}, + ItemKind, + }, }; #[test] fn parse_simple() { let src = "use foo;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Import(use_tree, visibility) = &item.kind else { @@ -133,7 +137,7 @@ mod tests { fn parse_simple_pub() { let src = "pub use foo;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Import(_, visibility) = &item.kind else { @@ -146,7 +150,7 @@ mod tests { fn parse_simple_pub_crate() { let src = "pub(crate) use foo;"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Import(_, visibility) = &item.kind else { @@ -159,7 +163,7 @@ mod tests { fn parse_simple_with_alias() { let src = "use foo as bar;"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + 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 { @@ -179,7 +183,7 @@ mod tests { fn parse_with_crate_prefix() { let src = "use crate::foo;"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + 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 { @@ -199,7 +203,7 @@ mod tests { fn parse_with_dep_prefix() { let src = "use dep::foo;"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + 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 { @@ -219,7 +223,7 @@ mod tests { fn parse_with_super_prefix() { let src = "use super::foo;"; let (mut module, errors) = parse_program(src); - assert!(errors.is_empty()); + 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 { @@ -239,7 +243,7 @@ mod tests { fn parse_list() { let src = "use foo::{bar, baz};"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Import(use_tree, visibility) = &item.kind else { @@ -258,7 +262,7 @@ mod tests { fn parse_list_trailing_comma() { let src = "use foo::{bar, baz, };"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Import(use_tree, visibility) = &item.kind else { @@ -277,7 +281,7 @@ mod tests { fn parse_list_that_starts_with_crate() { let src = "use crate::{foo, bar};"; let (module, errors) = parse_program(src); - assert!(errors.is_empty()); + expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Import(use_tree, visibility) = &item.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 6d68ecdbfeb..c4b480832a5 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -35,9 +35,7 @@ impl<'a> Parser<'a> { trailing_comma = self.eat_commas(); } - if constraints.is_empty() { - // TODO: error? (`where` but no constrains) - } + // Constraints might end up being empty, but that's accepted as valid syntax constraints } @@ -90,14 +88,14 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::Parser; + use crate::parser::{parser::tests::expect_no_errors, Parser}; #[test] fn parses_no_where_clause() { let src = "{"; let mut parser = Parser::for_str(src); let constraints = parser.parse_where_clause(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert!(constraints.is_empty()); } @@ -106,7 +104,7 @@ mod tests { let src = "where Foo: Bar + Baz"; let mut parser = Parser::for_str(src); let mut constraints = parser.parse_where_clause(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(constraints.len(), 2); let constraint = constraints.remove(0); @@ -124,7 +122,7 @@ mod tests { let src = "where Foo: Bar, i32: Qux"; let mut parser = Parser::for_str(src); let mut constraints = parser.parse_where_clause(); - assert!(parser.errors.is_empty()); + expect_no_errors(&parser.errors); assert_eq!(constraints.len(), 2); let constraint = constraints.remove(0); From b7651f48434b81563c05cae6defbe345cab4f578 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 11:18:17 -0300 Subject: [PATCH 173/229] Some recovery for items and structs --- compiler/noirc_frontend/src/parser/parser.rs | 4 +- .../src/parser/parser/expression.rs | 8 +-- .../src/parser/parser/function.rs | 8 +-- .../src/parser/parser/generics.rs | 4 +- .../src/parser/parser/global.rs | 6 +- .../noirc_frontend/src/parser/parser/impls.rs | 4 +- .../noirc_frontend/src/parser/parser/item.rs | 23 ++++++- .../src/parser/parser/item_visibility.rs | 8 +-- .../src/parser/parser/lambda.rs | 2 +- .../src/parser/parser/statement.rs | 6 +- .../src/parser/parser/structs.rs | 67 ++++++++++++++++--- .../noirc_frontend/src/parser/parser/tests.rs | 23 ++++++- .../src/parser/parser/traits.rs | 2 +- .../noirc_frontend/src/parser/parser/types.rs | 4 +- 14 files changed, 126 insertions(+), 43 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index f208b54aa85..6022f06c02a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -55,7 +55,7 @@ where F: FnOnce(&mut Parser<'a>) -> T, { let item = f(&mut parser); - if !parser.is_eof() { + if !parser.at_eof() { parser.expected_token(Token::EOF); return Err(parser.errors); } @@ -428,7 +428,7 @@ impl<'a> Parser<'a> { } } - fn is_eof(&self) -> bool { + fn at_eof(&self) -> bool { self.token.token() == &Token::EOF } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a172808e412..f6a128c0cef 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -672,7 +672,7 @@ mod tests { UnresolvedTypeData, }, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -906,7 +906,7 @@ mod tests { let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); parser.parse_expression(); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedExpressionAfterThis)); } @@ -919,7 +919,7 @@ mod tests { let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); parser.parse_expression(); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); }; @@ -1360,7 +1360,7 @@ mod tests { let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); parser.parse_expression(); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedTypeAfterThis)); } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 290cf95195f..b0aee747ec6 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -144,7 +144,7 @@ impl<'a> Parser<'a> { self.expected_label(ParsingRuleLabel::Pattern); // Let's try with the next token self.next_token(); - if self.is_eof() { + if self.at_eof() { break; } else { continue; @@ -294,7 +294,7 @@ mod tests { parser::{ parser::{ parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, }, ItemKind, ParserErrorReason, }, @@ -450,7 +450,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let (_, errors) = parse_program(&src); - let reason = get_single_error(&errors, span); + let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); } @@ -462,7 +462,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let (_, errors) = parse_program(&src); - let reason = get_single_error(&errors, span); + let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedFunctionBody)); } } diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 9413ddc0074..e6fb9f59332 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -172,7 +172,7 @@ mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -264,7 +264,7 @@ mod tests { let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); parser.parse_generics(); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); } diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index 8e8e1a1c537..135be6d22cf 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -74,7 +74,7 @@ mod tests { parser::{ parser::{ parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, }, ItemKind, ParserErrorReason, }, @@ -160,7 +160,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let (_, errors) = parse_program(&src); - let reason = get_single_error(&errors, span); + let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::GlobalWithoutValue)); } @@ -172,7 +172,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let (_, errors) = parse_program(&src); - let reason = get_single_error(&errors, span); + let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedSemicolonAfterGlobal)); } } diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 22e4762a51e..afb6caaec65 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -62,7 +62,7 @@ impl<'a> Parser<'a> { break; } - if self.is_eof() { + if self.at_eof() { self.expected_token(Token::RightBrace); break; } @@ -136,7 +136,7 @@ impl<'a> Parser<'a> { if self.eat_right_brace() { break; } - } else if self.is_eof() || self.eat_right_brace() { + } else if self.at_eof() || self.eat_right_brace() { break; } else { self.expected_label(ParsingRuleLabel::TraitImplItem); diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 41d656c8ac4..0dce3486e77 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -20,7 +20,7 @@ impl<'a> Parser<'a> { break; } - if self.is_eof() { + if self.at_eof() { break; } @@ -158,3 +158,24 @@ impl<'a> Parser<'a> { } } } + +#[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"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index f4214e65dd1..8dfb6016d76 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -38,7 +38,7 @@ mod tests { use crate::{ ast::ItemVisibility, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -71,7 +71,7 @@ mod tests { let mut parser = Parser::for_str(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedCrateAfterPub)); } @@ -85,7 +85,7 @@ mod tests { let mut parser = Parser::for_str(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedCrateAfterPub)); } #[test] @@ -98,7 +98,7 @@ mod tests { let mut parser = Parser::for_str(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::PublicCrate); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedParenAfterPubCrate)); } diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index dd8d2bedd07..c1fb0d5937d 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -38,7 +38,7 @@ impl<'a> Parser<'a> { // Let's try with the next token. self.next_token(); - if self.is_eof() { + if self.at_eof() { break; } else { continue; diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 09dd2e65528..5b214e611a8 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -359,7 +359,7 @@ mod tests { use crate::{ ast::{ConstrainKind, ExpressionKind, ForRange, LValue, StatementKind, UnresolvedTypeData}, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, Parser, ParserErrorReason, }, }; @@ -453,7 +453,7 @@ mod tests { assert_eq!(constrain.kind, ConstrainKind::Constrain); assert_eq!(constrain.arguments.len(), 1); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ConstrainDeprecated)); } @@ -625,7 +625,7 @@ mod tests { 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(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::EarlyReturn)); } } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 4d87397ff20..493e6bd2d50 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -42,20 +42,43 @@ impl<'a> Parser<'a> { let mut fields = Vec::new(); let mut trailing_comma = false; - loop { - let doc_comments_start_span = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); + 'outer: loop { + 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; + } - let start_span = self.current_token_span; - let Some(name) = self.eat_ident() else { if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, self.span_since(doc_comments_start_span), ); } - break; - }; + + // Though we do have to stop at EOF + if self.at_eof() { + self.expected_token(Token::RightBrace); + break 'outer; + } + + // Or if we find a right brace + if self.eat_right_brace() { + break 'outer; + } + + self.expected_identifier(); + self.next_token(); + } + + let start_span = self.previous_token_span; self.eat_or_error(Token::Colon); @@ -70,8 +93,6 @@ impl<'a> Parser<'a> { trailing_comma = self.eat_commas(); } - self.eat_or_error(Token::RightBrace); - NoirStruct { name, attributes, @@ -108,7 +129,10 @@ mod tests { parser::{ parser::{ parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, ItemKind, ParserErrorReason, }, @@ -235,7 +259,28 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let (_, errors) = parse_program(&src); - let reason = get_single_error(&errors, span); + 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/tests.rs b/compiler/noirc_frontend/src/parser/parser/tests.rs index 1de8f34ee57..ea8b1fc638d 100644 --- a/compiler/noirc_frontend/src/parser/parser/tests.rs +++ b/compiler/noirc_frontend/src/parser/parser/tests.rs @@ -20,10 +20,27 @@ pub(super) fn get_source_with_error_span(src: &str) -> (String, Span) { (src, span) } -pub(super) fn get_single_error(errors: &[ParserError], expected_span: Span) -> &ParserErrorReason { - assert_eq!(errors.len(), 1); +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].reason().unwrap() + &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]) { diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index a38e6364f97..ed5e117a828 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -53,7 +53,7 @@ impl<'a> Parser<'a> { break; } - if self.is_eof() { + if self.at_eof() { self.expected_token(Token::RightBrace); break; } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 36036162f54..f43f38b1675 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -456,7 +456,7 @@ mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, Parser, ParserErrorReason, }, QuotedType, @@ -635,7 +635,7 @@ mod tests { let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); parser.parse_type(); - let reason = get_single_error(&parser.errors, span); + let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedBracketAfterSliceType)); } From 4854662d95dc8e6ada546f6e1481eaeef3cd1078 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 11:23:50 -0300 Subject: [PATCH 174/229] Recover on unknown impl item --- .../noirc_frontend/src/parser/parser/impls.rs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index afb6caaec65..efa1629ae3b 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -250,7 +250,10 @@ mod tests { use crate::{ ast::{ItemVisibility, Pattern, TraitImplItemKind, UnresolvedTypeData}, parser::{ - parser::{parse_program, tests::expect_no_errors}, + parser::{ + parse_program, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + }, ItemKind, }, }; @@ -571,4 +574,24 @@ mod tests { 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 struct"); + }; + 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"); + } } From d13487d026e1634b0078289f3f5ffa89097ffc87 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 11:26:06 -0300 Subject: [PATCH 175/229] Recover un known trait impl item --- .../noirc_frontend/src/parser/parser/impls.rs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index efa1629ae3b..ae1189cf105 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -587,11 +587,31 @@ mod tests { assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Impl(type_impl) = &item.kind else { - panic!("Expected struct"); + 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"); + } } From 46c55be18f55741d98b22e38f61bc7439c86c49e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 11:41:25 -0300 Subject: [PATCH 176/229] Statement recovery --- compiler/noirc_frontend/src/parser/parser.rs | 4 + .../src/parser/parser/statement.rs | 80 ++++++++++++++----- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 6022f06c02a..e6238e2d56e 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -428,6 +428,10 @@ impl<'a> Parser<'a> { } } + fn at(&self, token: Token) -> bool { + self.token.token() == &token + } + fn at_eof(&self) -> bool { self.token.token() == &Token::EOF } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 5b214e611a8..a70e278c81d 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -6,7 +6,7 @@ use crate::{ ExpressionKind, ForLoopStatement, ForRange, Ident, InfixExpression, LValue, LetStatement, Statement, StatementKind, }, - parser::ParserErrorReason, + parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Attribute, Keyword, Token, TokenKind}, }; @@ -26,27 +26,36 @@ impl<'a> Parser<'a> { } pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { - let attributes = self.parse_attributes(); + loop { + let attributes = self.parse_attributes(); + let start_span = self.current_token_span; + let kind = self.parse_statement_kind(attributes); - let start_span = self.current_token_span; - let kind = self.parse_statement_kind(attributes)?; - let span = self.span_since(start_span); - let mut statement = Statement { kind, span }; + let (semicolon_token, semicolon_span) = if self.token.token() == &Token::Semicolon { + let token = self.token.clone(); + self.next_token(); + let span = token.to_span(); - let (token, span) = if self.token.token() == &Token::Semicolon { - let token = self.token.clone(); - self.next_token(); - let span = token.to_span(); + (Some(token.into_token()), span) + } else { + (None, self.previous_token_span) + }; - // Adjust the statement span to include the semicolon - statement.span = Span::from(statement.span.start()..span.end()); + let span = self.span_since(start_span); - (Some(token.into_token()), span) - } else { - (None, self.previous_token_span) - }; + if let Some(kind) = kind { + let statement = Statement { kind, span }; + return Some((statement, (semicolon_token, semicolon_span))); + } + + self.expected_label(ParsingRuleLabel::Statement); - Some((statement, (token, span))) + if semicolon_token.is_some() || self.at(Token::RightBrace) || self.at_eof() { + return None; + } else { + self.next_token(); + } + } } fn parse_statement_kind( @@ -359,7 +368,10 @@ mod tests { use crate::{ ast::{ConstrainKind, ExpressionKind, ForRange, LValue, StatementKind, UnresolvedTypeData}, parser::{ - parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, Parser, ParserErrorReason, }, }; @@ -628,4 +640,36 @@ mod tests { 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); + } } From 1afbafbfb958b4079dbab56f3324cbf5ea7b7ed2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 11:56:28 -0300 Subject: [PATCH 177/229] `mod` parsing recovery --- .../noirc_frontend/src/parser/parser/item.rs | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 0dce3486e77..9b6da700e69 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -16,10 +16,13 @@ impl<'a> Parser<'a> { let mut items = Vec::new(); loop { + // We only break out of the loop on `}` if we are inside a `mod { ..` if nested && self.token.token() == &Token::RightBrace { break; } + // 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() { break; } @@ -29,16 +32,25 @@ impl<'a> Parser<'a> { continue; } - let is_error_token = match self.token.token() { - Token::RightBrace => !nested, - Token::EOF => false, - _ => true, - }; - - if is_error_token { - self.expected_label(ParsingRuleLabel::Item); - // We'll try parsing an item on the next token - self.next_token(); + // If we couldn't parse an item we check which token we got + match self.token.token() { + Token::RightBrace => { + if nested { + break; + } else { + self.expected_label(ParsingRuleLabel::Item); + // We'll try parsing an item starting on the next token + self.next_token(); + } + } + Token::EOF => { + break; + } + _ => { + self.expected_label(ParsingRuleLabel::Item); + // We'll try parsing an item starting on the next token + self.next_token(); + } } } @@ -178,4 +190,17 @@ mod tests { 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"); + } } From 95d2647da7fcb1fb0ece5a4de593eec0ec91d963 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 12:20:14 -0300 Subject: [PATCH 178/229] Fix missing error --- compiler/noirc_frontend/src/parser/parser/types.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index f43f38b1675..fa78882dd86 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -408,10 +408,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let Some(typ) = self.parse_type() else { - if types.is_empty() { - self.expected_label(ParsingRuleLabel::Type); - } - + self.expected_label(ParsingRuleLabel::Type); self.eat_right_paren(); break; }; @@ -456,7 +453,9 @@ mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, parser::{ - parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, Parser, ParserErrorReason, }, QuotedType, @@ -584,7 +583,7 @@ mod tests { let src = "(Field"; let mut parser = Parser::for_str(src); let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); // TODO: there should be an error here + assert_eq!(parser.errors.len(), 1); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; From e5e735dd92e11030ced837917abc281b010767cf Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 12:36:51 -0300 Subject: [PATCH 179/229] Errors clean up --- compiler/noirc_frontend/src/parser/errors.rs | 32 ---------- compiler/noirc_frontend/src/parser/labels.rs | 2 + .../src/parser/parser/expression.rs | 62 +++++++------------ .../src/parser/parser/function.rs | 11 +--- .../src/parser/parser/global.rs | 22 +++---- .../noirc_frontend/src/parser/parser/infix.rs | 16 ++--- .../src/parser/parser/item_visibility.rs | 39 ++++++------ .../src/parser/parser/statement.rs | 5 +- .../src/parser/parser/type_expression.rs | 15 ++--- .../noirc_frontend/src/parser/parser/types.rs | 37 ++++------- 10 files changed, 84 insertions(+), 157 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 47d5cec97a6..8ca22ba88fa 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -48,42 +48,10 @@ pub enum ParserErrorReason { MissingTypeForFunctionParameter, #[error("Missing type for numeric generic")] MissingTypeForNumericGeneric, - #[error("Expected `)`")] - ExpectedRightParen, - #[error("Expected `(`")] - ExpectedLeftParen, - #[error("Expected an integer")] - ExpectedInteger, - #[error("Expected `crate` after `pub`")] - ExpectedCrateAfterPub, - #[error("Expected `)` after `pub(crate`")] - ExpectedParenAfterPubCrate, #[error("Expected a function body (`{{ ... }}`), not `;`")] ExpectedFunctionBody, #[error("Expected the global to have a value")] GlobalWithoutValue, - #[error("Expected `;` after a global declaration")] - ExpectedSemicolonAfterGlobal, - #[error("Expected `]` after slice type")] - ExpectedBracketAfterSliceType, - #[error("Expected `]` after array type")] - ExpectedBracketAfterArrayType, - #[error("Expected `]` after array")] - ExpectedBracketAfterArray, - #[error("Expected `]` after slice")] - ExpectedBracketAfterSlice, - #[error("Expected `>` after str type length")] - ExpectedGreaterAfterStringTypeLength, - #[error("Expected `{{` or `if` after `else`")] - ExpectedLeftBraceOfIfAfterElse, - #[error("Expected a type after this")] - ExpectedTypeAfterThis, - #[error("Expected an expression after this")] - ExpectedExpressionAfterThis, - #[error("Expected a statement after this")] - ExpectedStatementAfterThis, - #[error("Expected a type expression after this")] - ExpectedTypeExpressionAfterThis, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), diff --git a/compiler/noirc_frontend/src/parser/labels.rs b/compiler/noirc_frontend/src/parser/labels.rs index 4d23ef103fa..82aaeadb4fd 100644 --- a/compiler/noirc_frontend/src/parser/labels.rs +++ b/compiler/noirc_frontend/src/parser/labels.rs @@ -13,6 +13,7 @@ pub enum ParsingRuleLabel { FieldAccess, Global, Identifier, + Integer, IntegerType, Item, LValue, @@ -41,6 +42,7 @@ impl fmt::Display for ParsingRuleLabel { ParsingRuleLabel::FieldAccess => write!(f, "field access"), 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"), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index f6a128c0cef..d7d508afd70 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -35,7 +35,7 @@ impl<'a> Parser<'a> { if let Some(expr) = self.parse_expression_impl(allow_constructors) { expr } else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); Expression { kind: ExpressionKind::Error, span: self.span_at_previous_token_end() } } } @@ -48,10 +48,7 @@ impl<'a> Parser<'a> { 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.push_error( - ParserErrorReason::ExpectedExpressionAfterThis, - self.previous_token_span, - ); + self.expected_label(ParsingRuleLabel::Expression); return None; }; let kind = ExpressionKind::prefix(operator, rhs); @@ -376,10 +373,7 @@ impl<'a> Parser<'a> { } else if let Some(if_expr) = self.parse_if_expr() { Some(Expression { kind: if_expr, span: self.span_since(start_span) }) } else { - self.push_error( - ParserErrorReason::ExpectedLeftBraceOfIfAfterElse, - self.current_token_span, - ); + self.expected_token(Token::LeftBrace); None } } else { @@ -502,7 +496,7 @@ impl<'a> Parser<'a> { self.next_token(); return Some(ExpressionKind::Literal(Literal::Slice( - self.parse_array_literal(true).unwrap(), + self.parse_array_literal().unwrap(), ))); } @@ -514,11 +508,11 @@ impl<'a> Parser<'a> { } fn parse_array_expression(&mut self) -> Option { - self.parse_array_literal(false) + self.parse_array_literal() .map(|array_literal| ExpressionKind::Literal(Literal::Array(array_literal))) } - fn parse_array_literal(&mut self, is_slice: bool) -> Option { + fn parse_array_literal(&mut self) -> Option { if !self.eat_left_bracket() { return None; } @@ -534,19 +528,7 @@ impl<'a> Parser<'a> { if self.eat_semicolon() { let length = self.parse_expression_or_error(); - if !self.eat_right_bracket() { - if is_slice { - self.push_error( - ParserErrorReason::ExpectedBracketAfterSlice, - self.current_token_span, - ); - } else { - self.push_error( - ParserErrorReason::ExpectedBracketAfterArray, - self.current_token_span, - ); - } - } + self.eat_or_error(Token::RightBracket); return Some(ArrayLiteral::Repeated { repeated_element: Box::new(first_expr), length: Box::new(length), @@ -592,10 +574,7 @@ impl<'a> Parser<'a> { loop { let start_span = self.current_token_span; let Some(expr) = self.parse_expression() else { - self.push_error( - ParserErrorReason::ExpectedExpressionAfterThis, - self.previous_token_span, - ); + self.expected_label(ParsingRuleLabel::Expression); self.eat_right_paren(); break; }; @@ -657,8 +636,8 @@ impl<'a> Parser<'a> { }) } - pub(super) fn push_expected_expression_after_this_error(&mut self) { - self.push_error(ParserErrorReason::ExpectedExpressionAfterThis, self.previous_token_span); + pub(super) fn push_expected_expression(&mut self) { + self.expected_label(ParsingRuleLabel::Expression); } } @@ -672,7 +651,10 @@ mod tests { UnresolvedTypeData, }, parser::{ - parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, Parser, ParserErrorReason, }, }; @@ -900,14 +882,14 @@ mod tests { #[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 reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ExpectedExpressionAfterThis)); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected an expression but found end of input"); } #[test] @@ -1354,14 +1336,14 @@ mod tests { #[test] fn parses_cast_missing_type() { let src = " - 1 as - ^^ + 1 as + ^ "; 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); - assert!(matches!(reason, ParserErrorReason::ExpectedTypeAfterThis)); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a type but found end of input"); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index b0aee747ec6..19abe2d6e9f 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -227,22 +227,17 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::CallData) { if self.eat_left_paren() { if let Some(int) = self.eat_int() { - if !self.eat_right_paren() { - self.push_error( - ParserErrorReason::ExpectedRightParen, - self.current_token_span, - ); - } + self.eat_or_error(Token::RightParen); let id = int.to_u128() as u32; return Visibility::CallData(id); } else { - self.push_error(ParserErrorReason::ExpectedInteger, self.current_token_span); + self.expected_label(ParsingRuleLabel::Integer); self.eat_right_paren(); return Visibility::CallData(0); } } else { - self.push_error(ParserErrorReason::ExpectedLeftParen, self.current_token_span); + self.expected_token(Token::LeftParen); return Visibility::CallData(0); } } diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index 135be6d22cf..c93f6c3632a 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -6,7 +6,7 @@ use crate::{ UnresolvedTypeData, }, parser::ParserErrorReason, - token::Attribute, + token::{Attribute, Token}, }; use super::Parser; @@ -48,11 +48,8 @@ impl<'a> Parser<'a> { Expression { kind: ExpressionKind::Error, span: Span::default() } }; - if !self.eat_semicolon() { - self.push_error( - ParserErrorReason::ExpectedSemicolonAfterGlobal, - self.current_token_span, - ); + if !self.eat_semicolons() { + self.expected_token(Token::Semicolon); } LetStatement { pattern, r#type: typ, expression, attributes, comptime } @@ -74,7 +71,10 @@ mod tests { parser::{ parser::{ parse_program, - tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, + tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, ItemKind, ParserErrorReason, }, @@ -167,12 +167,12 @@ mod tests { #[test] fn parse_global_no_semicolon() { let src = " - global foo = 1 - ^ + global foo = 1 + ^ "; 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::ExpectedSemicolonAfterGlobal)); + 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/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 07794c8a637..222db1f7116 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -26,7 +26,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_or(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; @@ -54,7 +54,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_and(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; @@ -82,7 +82,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_xor(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; @@ -110,7 +110,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_less_or_greater(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; @@ -140,7 +140,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_shift(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; @@ -173,7 +173,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_add_or_subtract(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; @@ -203,7 +203,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_multiply_or_divide_or_modulo(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; @@ -238,7 +238,7 @@ impl<'a> Parser<'a> { let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_term(allow_constructors) else { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; }; diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 8dfb6016d76..3bd1ab63975 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -1,4 +1,7 @@ -use crate::{ast::ItemVisibility, parser::ParserErrorReason, token::Keyword}; +use crate::{ + ast::ItemVisibility, + token::{Keyword, Token}, +}; use super::Parser; @@ -15,18 +18,12 @@ impl<'a> Parser<'a> { if !self.eat_keyword(Keyword::Crate) { // `pub(` or `pub()` - self.push_error(ParserErrorReason::ExpectedCrateAfterPub, self.current_token_span); + self.expected_token(Token::Keyword(Keyword::Crate)); self.eat_right_paren(); return ItemVisibility::Public; } - if !self.eat_right_paren() { - // `pub(crate` - self.push_error( - ParserErrorReason::ExpectedParenAfterPubCrate, - self.previous_token_span, - ); - } + self.eat_or_error(Token::RightParen); // `pub(crate)`` ItemVisibility::PublicCrate @@ -38,8 +35,8 @@ mod tests { use crate::{ ast::ItemVisibility, parser::{ - parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, - Parser, ParserErrorReason, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + Parser, }, }; @@ -64,15 +61,15 @@ mod tests { #[test] fn parses_public_visibility_unclosed_parentheses() { let src = " - pub( - ^ + 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 reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ExpectedCrateAfterPub)); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a crate but found end of input"); } #[test] @@ -85,21 +82,21 @@ mod tests { let mut parser = Parser::for_str(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); - let reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ExpectedCrateAfterPub)); + 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 - ^^^^^ + 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 reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ExpectedParenAfterPubCrate)); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a ) but found end of input"); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index a70e278c81d..1aa93cd6eb3 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -17,10 +17,7 @@ impl<'a> Parser<'a> { if let Some((statement, (_token, _span))) = self.parse_statement() { statement } else { - self.push_error( - ParserErrorReason::ExpectedStatementAfterThis, - self.previous_token_span, - ); + self.expected_label(ParsingRuleLabel::Statement); Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() } } } diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 2108dc31214..21be9afe643 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -54,7 +54,7 @@ impl<'a> Parser<'a> { ); } None => { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); } } } @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { ); } None => { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); break; } } @@ -122,7 +122,7 @@ impl<'a> Parser<'a> { )) } None => { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); None } }; @@ -257,7 +257,7 @@ impl<'a> Parser<'a> { Some(UnresolvedType { typ, span }) } None => { - self.push_expected_expression_after_this_error(); + self.push_expected_expression(); None } }; @@ -351,9 +351,10 @@ impl<'a> Parser<'a> { fn expected_type_expression_after_this( &mut self, ) -> Result { - Err(ParserError::with_reason( - ParserErrorReason::ExpectedTypeExpressionAfterThis, - self.previous_token_span, + Err(ParserError::expected_label( + ParsingRuleLabel::TypeExpression, + self.token.token().clone(), + self.current_token_span, )) } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index fa78882dd86..048bac0d21c 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -12,7 +12,7 @@ impl<'a> Parser<'a> { if let Some(typ) = self.parse_type() { typ } else { - self.push_error(ParserErrorReason::ExpectedTypeAfterThis, self.previous_token_span); + self.expected_label(ParsingRuleLabel::Type); self.unspecified_type_at_previous_token_end() } } @@ -149,12 +149,7 @@ impl<'a> Parser<'a> { } }; - if !self.eat_greater() { - self.push_error( - ParserErrorReason::ExpectedGreaterAfterStringTypeLength, - self.current_token_span, - ); - } + self.eat_or_error(Token::Greater); Some(UnresolvedTypeData::String(expr)) } @@ -370,25 +365,17 @@ impl<'a> Parser<'a> { if self.eat_semicolon() { match self.parse_type_expression() { Ok(expr) => { - if !self.eat_right_bracket() { - self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); - } + self.eat_or_error(Token::RightBracket); Some(UnresolvedTypeData::Array(expr, Box::new(typ))) } Err(error) => { self.errors.push(error); - if !self.eat_right_bracket() { - self.push_error(ParserErrorReason::ExpectedBracketAfterArrayType, typ.span); - } - + self.eat_or_error(Token::RightBracket); Some(UnresolvedTypeData::Slice(Box::new(typ))) } } } else { - if !self.eat_right_bracket() { - self.push_error(ParserErrorReason::ExpectedBracketAfterSliceType, typ.span); - } - + self.eat_or_error(Token::RightBracket); Some(UnresolvedTypeData::Slice(Box::new(typ))) } } @@ -453,10 +440,8 @@ mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, parser::{ - parser::tests::{ - expect_no_errors, get_single_error_reason, get_source_with_error_span, - }, - Parser, ParserErrorReason, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + Parser, }, QuotedType, }; @@ -628,14 +613,14 @@ mod tests { #[test] fn errors_if_missing_right_bracket_after_slice_type() { let src = " - [Field - ^^^^^ + [Field + ^ "; let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); parser.parse_type(); - let reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ExpectedBracketAfterSliceType)); + let error = get_single_error(&parser.errors, span); + assert_eq!(error.to_string(), "Expected a ] but found end of input"); } #[test] From c94fbd6a75c72761876f2cbd22fcae64d0de5f2f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 12:39:59 -0300 Subject: [PATCH 180/229] Clean up more errors --- compiler/noirc_frontend/src/parser/errors.rs | 12 ------------ .../noirc_frontend/src/parser/parser/expression.rs | 5 +---- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 8ca22ba88fa..33ea1453e6a 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -57,18 +57,6 @@ pub enum ParserErrorReason { ExpectedFieldName(Token), #[error("expected a pattern but found a type - {0}")] ExpectedPatternButFoundType(Token), - #[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")] diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index d7d508afd70..139474aeb92 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -351,10 +351,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let Some(consequence) = self.parse_block_expression() else { - self.push_error( - ParserErrorReason::ExpectedLeftBraceAfterIfCondition, - self.current_token_span, - ); + self.expected_token(Token::LeftBrace); let span = self.span_at_previous_token_end(); return Some(ExpressionKind::If(Box::new(IfExpression { condition, From 96fd3bd513cc74b79b88bfb9a1ad0c28a6effddb Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 12:42:17 -0300 Subject: [PATCH 181/229] Bring back `ExpectedFieldName` --- compiler/noirc_frontend/src/parser/errors.rs | 2 +- compiler/noirc_frontend/src/parser/parser/expression.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 33ea1453e6a..97e81998cc4 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -53,7 +53,7 @@ pub enum ParserErrorReason { #[error("Expected the global to have a value")] GlobalWithoutValue, - #[error("Unexpected '{0}', expected a field name")] + #[error("Unexpected '{0}', expected a field name or number")] ExpectedFieldName(Token), #[error("expected a pattern but found a type - {0}")] ExpectedPatternButFoundType(Token), diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 139474aeb92..7e8be1518b7 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -106,7 +106,10 @@ impl<'a> Parser<'a> { } else if let Some(int) = self.eat_int() { Ident::new(int.to_string(), self.previous_token_span) } else { - self.expected_identifier(); + self.push_error( + ParserErrorReason::ExpectedFieldName(self.token.token().clone()), + self.current_token_span, + ); continue; }; From 37bbdefab354b8a403b5b467f1af8adec2f59b34 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 12:54:08 -0300 Subject: [PATCH 182/229] Bring back ExpectedPatternButFoundType --- compiler/noirc_frontend/src/parser/errors.rs | 4 +- .../src/parser/parser/pattern.rs | 54 ++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 97e81998cc4..5b839b923d9 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -55,7 +55,7 @@ pub enum ParserErrorReason { #[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 a ; separating these two statements")] MissingSeparatingSemi, @@ -244,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, ), diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 918927cd525..5a89e577454 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -113,6 +113,12 @@ impl<'a> Parser<'a> { } 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; }; @@ -211,6 +217,28 @@ impl<'a> Parser<'a> { _ => unreachable!(), } } + + 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)] @@ -218,7 +246,13 @@ mod tests { use crate::{ ast::Pattern, - parser::{parser::tests::expect_no_errors, Parser}, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + token::{Keyword, Token}, }; #[test] @@ -314,4 +348,22 @@ mod tests { 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)) + )); + } } From 021ead8b50bcb08ea89a268a884ddabcbbb0ea36 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 13:02:07 -0300 Subject: [PATCH 183/229] Apparently function pattern recovery was already done --- .../src/parser/parser/function.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 19abe2d6e9f..b355691905e 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -289,7 +289,10 @@ mod tests { parser::{ parser::{ parse_program, - tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, + tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, ItemKind, ParserErrorReason, }, @@ -460,4 +463,22 @@ mod tests { 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); + + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a pattern but found 1"); + } } From 49419052b0aaf20ccc24b2dc68353bdcc0e4a48c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 13:11:07 -0300 Subject: [PATCH 184/229] Some more recovery --- .../src/parser/parser/function.rs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index b355691905e..7f120a59925 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -170,7 +170,7 @@ impl<'a> Parser<'a> { } else { self.push_error( ParserErrorReason::MissingTypeForFunctionParameter, - self.previous_token_span, + Span::from(pattern.span().start()..self.current_token_span.end()), ); parameters.push(Param { @@ -481,4 +481,40 @@ mod tests { 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 ,"); + } } From 83448f3fb15c2aeab1041b7838707dcd332ae33c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 13:38:57 -0300 Subject: [PATCH 185/229] cargo fmt --- compiler/noirc_frontend/src/parser/parser/generics.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index e6fb9f59332..7d3e42a4829 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -172,7 +172,9 @@ mod tests { use crate::{ ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ - parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, Parser, ParserErrorReason, }, }; From 8a76f2f33e4622d64fae4f8bd56589395d807de2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 14:30:32 -0300 Subject: [PATCH 186/229] Let `crate::`, etc., error and return no Path --- .../noirc_frontend/src/parser/parser/path.rs | 64 ++++++++++++++++--- .../src/parser/parser/use_tree.rs | 5 -- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index ee699ea3386..a5b561a3454 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -65,16 +65,50 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::DoubleColon); } - self.parse_path_after_kind(kind, allow_turbofish, allow_trailing_double_colon, start_span) + 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_path_after_kind( + 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) + } + } + + 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 { @@ -110,11 +144,7 @@ impl<'a> Parser<'a> { } } - if segments.is_empty() && kind == PathKind::Plain { - None - } else { - Some(Path { segments, kind, span: self.span_since(start_span) }) - } + Path { segments, kind, span: self.span_since(start_span) } } pub(super) fn parse_path_generics( @@ -172,7 +202,10 @@ mod tests { use crate::{ ast::PathKind, - parser::{parser::tests::expect_no_errors, Parser}, + parser::{ + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, + Parser, + }, }; #[test] @@ -292,4 +325,19 @@ mod tests { 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()); + + 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/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index f21f4ff2765..10a98c6b3d8 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -34,11 +34,6 @@ impl<'a> Parser<'a> { false, // allow trailing double colon start_span, ); - let prefix = prefix.unwrap_or_else(|| Path { - segments: Vec::new(), - kind: PathKind::Plain, - span: self.span_since(start_span), - }); let trailing_double_colon = if prefix.segments.is_empty() && kind != PathKind::Plain { true From 4ad85dd278257490ee9bb7029f390a6508aafce5 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 14:33:49 -0300 Subject: [PATCH 187/229] Add some comments --- compiler/noirc_frontend/src/parser/parser/path.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index a5b561a3454..90de6c56a50 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -25,6 +25,8 @@ impl<'a> Parser<'a> { } } + /// Tries to parse a Path. + /// Note that `crate::`, `super::`, etc., are not valid paths on their own. pub(crate) fn parse_path(&mut self) -> Option { self.parse_path_impl( true, // allow turbofish @@ -102,6 +104,9 @@ impl<'a> Parser<'a> { } } + /// 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, From ad89c0a2e624ff4689b9bfb66ef1c0c4b16dc8b4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 15:37:30 -0300 Subject: [PATCH 188/229] Make it more readable --- compiler/noirc_frontend/src/parser/parser.rs | 16 ++++++++ .../src/parser/parser/expression.rs | 15 +++----- .../src/parser/parser/generics.rs | 4 +- .../noirc_frontend/src/parser/parser/infix.rs | 37 +++++++++---------- .../noirc_frontend/src/parser/parser/item.rs | 2 +- .../noirc_frontend/src/parser/parser/path.rs | 5 +-- .../src/parser/parser/pattern.rs | 6 +-- .../src/parser/parser/statement.rs | 13 +++---- .../src/parser/parser/type_expression.rs | 3 +- 9 files changed, 51 insertions(+), 50 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index e6238e2d56e..c6b8910b1a1 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -50,6 +50,9 @@ pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { (program, errors) } +/// Invokes `f` with the given parser (`f` 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<'a, T, F>(mut parser: Parser<'a>, f: F) -> Result> where F: FnOnce(&mut Parser<'a>) -> T, @@ -134,6 +137,7 @@ impl<'a> Parser<'a> { ParsedModule { items, inner_doc_comments } } + /// 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; @@ -432,6 +436,18 @@ impl<'a> Parser<'a> { self.token.token() == &token } + fn at_keyword(&self, keyword: Keyword) -> bool { + self.at(Token::Keyword(keyword)) + } + + fn next_is(&self, token: Token) -> bool { + self.next_token.token() == &token + } + + fn tokens_follow(&self, token: Token, next_token: Token) -> bool { + self.at(token) && self.next_is(next_token) + } + fn at_eof(&self) -> bool { self.token.token() == &Token::EOF } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 7e8be1518b7..27ee840a273 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -60,9 +60,7 @@ impl<'a> Parser<'a> { } fn parse_unary_op(&mut self) -> Option { - if self.token.token() == &Token::Ampersand - && self.next_token.token() == &Token::Keyword(Keyword::Mut) - { + if self.tokens_follow(Token::Ampersand, Token::Keyword(Keyword::Mut)) { self.next_token(); self.next_token(); Some(UnaryOp::MutableReference) @@ -82,8 +80,7 @@ impl<'a> Parser<'a> { let mut atom = self.parse_atom(allow_constructors)?; loop { - let is_macro_call = - self.token.token() == &Token::Bang && self.next_token.token() == &Token::LeftParen; + let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); if is_macro_call { // Next `self.parse_arguments` will return `Some(...)` self.next_token(); @@ -125,8 +122,7 @@ impl<'a> Parser<'a> { None }; - let is_macro_call = self.token.token() == &Token::Bang - && self.next_token.token() == &Token::LeftParen; + let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); if is_macro_call { // Next `self.parse_arguments` will return `Some(...)` self.next_token(); @@ -202,7 +198,7 @@ impl<'a> Parser<'a> { } if matches!(self.token.token(), Token::InternedUnresolvedTypeData(..)) - && self.next_token.token() == &Token::LeftBrace + && self.next_is(Token::LeftBrace) { let span = self.current_token_span; let typ = self.parse_interned_type().unwrap(); @@ -491,8 +487,7 @@ impl<'a> Parser<'a> { } // Check if it's `&[` - if self.token.token() == &Token::Ampersand && self.next_token.token() == &Token::LeftBracket - { + if self.tokens_follow(Token::Ampersand, Token::LeftBracket) { self.next_token(); return Some(ExpressionKind::Literal(Literal::Slice( diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 7d3e42a4829..307d6c95443 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -123,9 +123,7 @@ impl<'a> Parser<'a> { loop { let start_span = self.current_token_span; - if matches!(self.token.token(), Token::Ident(..)) - && self.next_token.token() == &Token::Assign - { + if matches!(self.token.token(), Token::Ident(..)) && self.next_is(Token::Assign) { let ident = self.eat_ident().unwrap(); if !trailing_comma && !generic_type_args.is_empty() { diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 222db1f7116..c0357763db4 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -41,7 +41,7 @@ impl<'a> Parser<'a> { let mut lhs = self.parse_and(allow_constructors)?; // Don't parse `x |= ...`, etc. - if self.next_token.token() == &Token::Assign { + if self.next_is(Token::Assign) { return Some(lhs); } @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { let mut lhs = self.parse_xor(allow_constructors)?; // Don't parse `x &= ...`, etc. - if self.next_token.token() == &Token::Assign { + if self.next_is(Token::Assign) { return Some(lhs); } @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { let mut lhs = self.parse_less_or_greater(allow_constructors)?; // Don't parse `x ^= ...`, etc. - if self.next_token.token() == &Token::Assign { + if self.next_is(Token::Assign) { return Some(lhs); } @@ -155,21 +155,18 @@ impl<'a> Parser<'a> { let mut lhs = self.parse_add_or_subtract(allow_constructors)?; loop { - let operator = - if self.next_token.token() != &Token::Assign && self.eat(Token::ShiftLeft) { - BinaryOpKind::ShiftLeft - } else if self.token.token() == &Token::Greater - && self.next_token.token() == &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. - self.next_token(); - self.next_token(); - BinaryOpKind::ShiftRight - } else { - break; - }; + let operator = if !self.next_is(Token::Assign) && self.eat(Token::ShiftLeft) { + BinaryOpKind::ShiftLeft + } else if self.tokens_follow(Token::Greater, 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. + self.next_token(); + self.next_token(); + BinaryOpKind::ShiftRight + } else { + break; + }; let operator = Spanned::from(self.previous_token_span, operator); let Some(rhs) = self.parse_add_or_subtract(allow_constructors) else { @@ -188,7 +185,7 @@ impl<'a> Parser<'a> { let mut lhs = self.parse_multiply_or_divide_or_modulo(allow_constructors)?; // Don't parse `x += ...`, etc. - if self.next_token.token() == &Token::Assign { + if self.next_is(Token::Assign) { return Some(lhs); } @@ -221,7 +218,7 @@ impl<'a> Parser<'a> { let mut lhs = self.parse_term(allow_constructors)?; // Don't parse `x *= ...`, etc. - if self.next_token.token() == &Token::Assign { + if self.next_is(Token::Assign) { return Some(lhs); } diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 9b6da700e69..74534867af1 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -17,7 +17,7 @@ impl<'a> Parser<'a> { loop { // We only break out of the loop on `}` if we are inside a `mod { ..` - if nested && self.token.token() == &Token::RightBrace { + if nested && self.at(Token::RightBrace) { break; } diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 90de6c56a50..23fee65cfe9 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -122,8 +122,7 @@ impl<'a> Parser<'a> { let span = ident.span(); let generics = if allow_turbofish - && self.token.token() == &Token::DoubleColon - && self.next_token.token() == &Token::Less + && self.tokens_follow(Token::DoubleColon, Token::Less) { self.next_token(); self.parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) @@ -133,7 +132,7 @@ impl<'a> Parser<'a> { segments.push(PathSegment { ident, generics, span }); - if self.token.token() == &Token::DoubleColon + if self.at(Token::DoubleColon) && matches!(self.next_token.token(), Token::Ident(..)) { // Skip the double colons diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 5a89e577454..b74f26b9487 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -58,9 +58,7 @@ impl<'a> Parser<'a> { } } - if self.token.token() == &Token::Ampersand - && self.next_token.token() == &Token::Keyword(Keyword::Mut) - { + if self.tokens_follow(Token::Ampersand, Token::Keyword(Keyword::Mut)) { self.next_token(); self.next_token(); if !self.next_is_colon() && self.eat_self() { @@ -83,7 +81,7 @@ impl<'a> Parser<'a> { } fn next_is_colon(&self) -> bool { - self.next_token.token() == &Token::Colon + self.next_is(Token::Colon) } pub(crate) fn parse_pattern_after_modifiers( diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 1aa93cd6eb3..e4dd7639a31 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let kind = self.parse_statement_kind(attributes); - let (semicolon_token, semicolon_span) = if self.token.token() == &Token::Semicolon { + let (semicolon_token, semicolon_span) = if self.at(Token::Semicolon) { let token = self.token.clone(); self.next_token(); let span = token.to_span(); @@ -84,7 +84,7 @@ impl<'a> Parser<'a> { return Some(StatementKind::Error); } - if self.token.token() == &Token::Keyword(Keyword::Let) { + if self.at_keyword(Keyword::Let) { let let_statement = self.parse_let_statement(attributes)?; return Some(StatementKind::Let(let_statement)); } @@ -93,7 +93,7 @@ impl<'a> Parser<'a> { return Some(StatementKind::Constrain(constrain)); } - if self.token.token() == &Token::Keyword(Keyword::Comptime) { + if self.at_keyword(Keyword::Comptime) { return self.parse_comptime_statement(attributes); } @@ -168,7 +168,7 @@ impl<'a> Parser<'a> { fn next_is_op_assign(&mut self) -> Option { let start_span = self.current_token_span; - let operator = if self.next_token.token() == &Token::Assign { + let operator = if self.next_is(Token::Assign) { match self.token.token() { Token::Plus => Some(BinaryOpKind::Add), Token::Minus => Some(BinaryOpKind::Subtract), @@ -181,9 +181,8 @@ impl<'a> Parser<'a> { Token::Pipe => Some(BinaryOpKind::Or), _ => None, } - } else if self.token.token() == &Token::Greater - && self.next_token.token() == &Token::GreaterEqual - { + } else if self.tokens_follow(Token::Greater, Token::GreaterEqual) { + // >>= Some(BinaryOpKind::ShiftRight) } else { None diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 21be9afe643..5d2271a201e 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -176,8 +176,7 @@ impl<'a> Parser<'a> { fn parse_parenthesized_type_expression(&mut self) -> Option { // Make sure not to parse `()` as a parenthesized expression - if self.token.token() == &Token::LeftParen && self.next_token.token() != &Token::RightParen - { + if self.at(Token::LeftParen) && !self.next_is(Token::RightParen) { self.next_token(); match self.parse_type_expression() { Ok(type_expr) => { From fcc6291e9f33b26015624cb1ad7ba116c301a50e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 1 Oct 2024 19:31:10 -0300 Subject: [PATCH 189/229] Test that crate isn't allowed in sub-trees in use --- .../src/parser/parser/use_tree.rs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 10a98c6b3d8..acd7f39dd96 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -17,7 +17,9 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::DoubleColon); } - let use_tree = self.parse_use_tree_without_kind(start_span, kind); + let use_tree = self.parse_use_tree_without_kind( + start_span, kind, false, // nested + ); if !self.eat_semicolons() { self.expected_token(Token::Semicolon); } @@ -28,13 +30,13 @@ impl<'a> Parser<'a> { &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 { @@ -48,8 +50,11 @@ impl<'a> Parser<'a> { loop { let start_span = self.current_token_span; - let use_tree = - self.parse_use_tree_without_kind(self.current_token_span, PathKind::Plain); + let use_tree = self.parse_use_tree_without_kind( + self.current_token_span, + PathKind::Plain, + true, // nested + ); // If we didn't advance at all, we are done if start_span == self.current_token_span { @@ -71,16 +76,20 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::List(use_trees) } } else { self.expected_token(Token::LeftBrace); - self.parse_path_use_tree_end(prefix) + self.parse_path_use_tree_end(prefix, nested) } } else { - self.parse_path_use_tree_end(prefix) + self.parse_path_use_tree_end(prefix, nested) } } - pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path) -> UseTree { + pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path, nested: bool) -> UseTree { if prefix.segments.is_empty() { - self.expected_label(ParsingRuleLabel::UseSegment); + 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; @@ -285,4 +294,11 @@ mod tests { 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()); + } } From 2e0c1fb867eef5022218c59965754fc1ccecdc0b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 08:42:43 -0300 Subject: [PATCH 190/229] Simplify parse_atom_or_unary_right --- .../src/parser/parser/expression.rs | 219 +++++++++++------- 1 file changed, 135 insertions(+), 84 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 27ee840a273..4f84697a8b2 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -78,100 +78,151 @@ impl<'a> Parser<'a> { 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 { - let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); - if is_macro_call { - // Next `self.parse_arguments` will return `Some(...)` - self.next_token(); - } - - if let Some(arguments) = self.parse_arguments() { - let kind = ExpressionKind::Call(Box::new(CallExpression { - func: Box::new(atom), - arguments, - is_macro_call, - })); - let span = self.span_since(start_span); - atom = Expression { kind, span }; + (atom, parsed) = self.parse_unary_right(atom, start_span); + if parsed { continue; + } else { + break; } + } - if self.eat_dot() { - let field_name = if let Some(ident) = self.eat_ident() { - ident - } else if let Some(int) = self.eat_int() { - Ident::new(int.to_string(), self.previous_token_span) - } else { - self.push_error( - ParserErrorReason::ExpectedFieldName(self.token.token().clone()), - self.current_token_span, - ); - continue; - }; - - let generics = 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 - }; - - let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); - if is_macro_call { - // Next `self.parse_arguments` will return `Some(...)` - self.next_token(); - } - - if let Some(arguments) = self.parse_arguments() { - let kind = ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object: atom, - method_name: field_name, - generics, - arguments, - is_macro_call, - })); - let span = self.span_since(start_span); - atom = Expression { kind, span }; - } else { - let kind = ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: atom, - rhs: field_name, - })); - let span = self.span_since(start_span); - atom = Expression { kind, span }; - } - continue; - } + Some(atom) + } - if self.eat_keyword(Keyword::As) { - 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); - atom = Expression { kind, span }; - continue; - } + fn parse_unary_right(&mut self, mut atom: Expression, start_span: Span) -> (Expression, bool) { + let mut parsed; - if self.eat_left_bracket() { - 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); - atom = Expression { kind, span }; - continue; - } + (atom, parsed) = self.parse_call(atom, start_span); + if parsed { + return (atom, parsed); + } - break; + (atom, parsed) = self.parse_member_access_or_method_call(atom, start_span); + if parsed { + return (atom, parsed); } - Some(atom) + (atom, parsed) = self.parse_cast(atom, start_span); + if parsed { + return (atom, parsed); + } + + self.parse_index(atom, start_span) + } + + fn parse_call(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); + if is_macro_call { + // Next `self.parse_arguments` will return `Some(...)` + self.next_token(); + } + + if let Some(arguments) = self.parse_arguments() { + let kind = ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(atom), + arguments, + is_macro_call, + })); + let span = self.span_since(start_span); + let atom = Expression { kind, span }; + (atom, true) + } else { + (atom, false) + } + } + + 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 is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); + if is_macro_call { + // Next `self.parse_arguments` will return `Some(...)` + self.next_token(); + } + + let kind = if let Some(arguments) = self.parse_arguments() { + ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: atom, + method_name: field_name, + generics, + 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 + } + } + + 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) + } + + 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 + } } fn parse_atom(&mut self, allow_constructors: bool) -> Option { From 411a970564538587e4464b6db71acaf5e646792d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 08:43:28 -0300 Subject: [PATCH 191/229] parse_block_expression -> parse_block --- .../noirc_frontend/src/parser/parser/expression.rs | 12 ++++++------ .../noirc_frontend/src/parser/parser/function.rs | 2 +- .../noirc_frontend/src/parser/parser/statement.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 4f84697a8b2..1f591546a16 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -336,7 +336,7 @@ impl<'a> Parser<'a> { } let start_span = self.current_token_span; - if let Some(block) = self.parse_block_expression() { + if let Some(block) = self.parse_block() { Some(ExpressionKind::Unsafe(block, self.span_since(start_span))) } else { Some(ExpressionKind::Error) @@ -400,7 +400,7 @@ impl<'a> Parser<'a> { let condition = self.parse_expression_no_constructors_or_error(); let start_span = self.current_token_span; - let Some(consequence) = self.parse_block_expression() else { + 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 { @@ -414,7 +414,7 @@ impl<'a> Parser<'a> { let alternative = if self.eat_keyword(Keyword::Else) { let start_span = self.current_token_span; - if let Some(alternative) = self.parse_block_expression() { + if let Some(alternative) = self.parse_block() { let span = self.span_since(start_span); Some(Expression { kind: ExpressionKind::Block(alternative), span }) } else if let Some(if_expr) = self.parse_if_expr() { @@ -437,7 +437,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; - let Some(block) = self.parse_block_expression() else { + let Some(block) = self.parse_block() else { self.expected_token(Token::LeftBrace); return None; }; @@ -546,7 +546,7 @@ impl<'a> Parser<'a> { ))); } - if let Some(kind) = self.parse_block_expression() { + if let Some(kind) = self.parse_block() { return Some(ExpressionKind::Block(kind)); } @@ -644,7 +644,7 @@ impl<'a> Parser<'a> { }) } - pub(super) fn parse_block_expression(&mut self) -> Option { + pub(super) fn parse_block(&mut self) -> Option { if !self.eat_left_brace() { return None; } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 9af0ab954ef..a18b3ba9dad 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -108,7 +108,7 @@ impl<'a> Parser<'a> { None } else { - Some(self.parse_block_expression().unwrap_or_else(empty_body)) + Some(self.parse_block().unwrap_or_else(empty_body)) }; FunctionDefinitionWithOptionalBody { diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index e4dd7639a31..b5a62467328 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -108,7 +108,7 @@ impl<'a> Parser<'a> { })); } - if let Some(block) = self.parse_block_expression() { + if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), span: self.span_since(start_span), @@ -224,7 +224,7 @@ impl<'a> Parser<'a> { }; let block_start_span = self.current_token_span; - let block = if let Some(block) = self.parse_block_expression() { + let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), span: self.span_since(block_start_span), @@ -281,7 +281,7 @@ impl<'a> Parser<'a> { ) -> Option { let start_span = self.current_token_span; - if let Some(block) = self.parse_block_expression() { + if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), span: self.span_since(start_span), From 5d4b464fa03a12a5ac0c30dff709b4713d04c416 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 09:17:13 -0300 Subject: [PATCH 192/229] Document the grammar of expressions --- .../src/parser/parser/expression.rs | 121 ++++++++++++++++-- .../noirc_frontend/src/parser/parser/infix.rs | 16 +++ 2 files changed, 123 insertions(+), 14 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 1f591546a16..4611aabdf00 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -18,6 +18,7 @@ impl<'a> Parser<'a> { 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 } @@ -44,8 +45,12 @@ impl<'a> Parser<'a> { 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); @@ -59,6 +64,7 @@ impl<'a> Parser<'a> { self.parse_atom_or_unary_right(allow_constructors) } + /// UnaryOp = '&' 'mut' | '-' | '!' | '*' fn parse_unary_op(&mut self) -> Option { if self.tokens_follow(Token::Ampersand, Token::Keyword(Keyword::Mut)) { self.next_token(); @@ -75,6 +81,9 @@ impl<'a> Parser<'a> { } } + /// 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)?; @@ -92,6 +101,11 @@ impl<'a> Parser<'a> { Some(atom) } + /// UnaryRightExpression + /// = CallExpression + /// | MemberAccessOrMethodCallExpression + /// | CastExpression + /// | IndexExpression fn parse_unary_right(&mut self, mut atom: Expression, start_span: Span) -> (Expression, bool) { let mut parsed; @@ -113,6 +127,7 @@ impl<'a> Parser<'a> { self.parse_index(atom, start_span) } + /// CallExpression = Atom '!'? Arguments fn parse_call(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); if is_macro_call { @@ -134,6 +149,13 @@ impl<'a> Parser<'a> { } } + /// MemberAccessOrMethodCallExpression + /// = MemberAccessExpression + /// | MethodCallExpression + /// + /// MemberAccessExpression = Atom '.' identifier + /// + /// MethodCallExpression = Atom '.' identifier '!'? Arguments fn parse_member_access_or_method_call( &mut self, atom: Expression, @@ -187,6 +209,7 @@ impl<'a> Parser<'a> { } } + /// 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); @@ -199,6 +222,7 @@ impl<'a> Parser<'a> { (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); @@ -225,6 +249,20 @@ impl<'a> Parser<'a> { } } + /// 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)?; @@ -248,6 +286,7 @@ impl<'a> Parser<'a> { 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) { @@ -297,6 +336,7 @@ impl<'a> Parser<'a> { None } + /// ResolvedExpression = unquote_marker fn parse_resolved_expr(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::UnquoteMarker) { match token.into_token() { @@ -308,6 +348,7 @@ impl<'a> Parser<'a> { None } + /// InternedExpression = interned_expr fn parse_interned_expr(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::InternedExpr) { match token.into_token() { @@ -319,6 +360,7 @@ impl<'a> Parser<'a> { 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() { @@ -330,6 +372,7 @@ impl<'a> Parser<'a> { None } + /// UnsafeExpression = 'unsafe' Block fn parse_unsafe_expr(&mut self) -> Option { if !self.eat_keyword(Keyword::Unsafe) { return None; @@ -343,6 +386,11 @@ impl<'a> Parser<'a> { } } + /// PathExpression + /// = VariableExpression + /// | ConstructorExpression + /// + /// VariableExpression = Path fn parse_path_expr(&mut self, allow_constructors: bool) -> Option { let Some(path) = self.parse_path() else { return None; @@ -356,6 +404,7 @@ impl<'a> Parser<'a> { Some(ExpressionKind::Variable(path)) } + /// ConstructorExpression = Type '{' (identifier (':' Expression)?)* '}' fn parse_constructor(&mut self, typ: UnresolvedType) -> ExpressionKind { let mut fields = Vec::new(); let mut trailing_comma = false; @@ -392,6 +441,7 @@ impl<'a> Parser<'a> { })) } + /// IfExpression = 'if' Expression Block ('else' (Block | IfExpression))? pub(super) fn parse_if_expr(&mut self) -> Option { if !self.eat_keyword(Keyword::If) { return None; @@ -430,6 +480,7 @@ impl<'a> Parser<'a> { 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; @@ -445,6 +496,9 @@ impl<'a> Parser<'a> { 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; @@ -479,6 +533,7 @@ impl<'a> Parser<'a> { 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 { @@ -508,6 +563,20 @@ impl<'a> Parser<'a> { Some(ExpressionKind::TypePath(TypePath { typ, item, turbofish })) } + /// Literal + /// = bool + /// | int + /// | str + /// | rawstr + /// | fmtstr + /// | QuoteExpression + /// | ArrayLiteralExpression + /// | SliceLiteralExpression + /// | BlockExpression + /// + /// QuoteExpression = 'quote' '{' token* '}' + /// + /// BlockExpression = Block fn parse_literal(&mut self) -> Option { if let Some(bool) = self.eat_bool() { return Some(ExpressionKind::Literal(Literal::Bool(bool))); @@ -533,17 +602,12 @@ impl<'a> Parser<'a> { return Some(ExpressionKind::Quote(tokens)); } - if let Some(kind) = self.parse_array_expression() { - return Some(kind); + if let Some(literal) = self.parse_array_literal() { + return Some(ExpressionKind::Literal(Literal::Array(literal))); } - // Check if it's `&[` - if self.tokens_follow(Token::Ampersand, Token::LeftBracket) { - self.next_token(); - - return Some(ExpressionKind::Literal(Literal::Slice( - self.parse_array_literal().unwrap(), - ))); + if let Some(literal) = self.parse_slice_literal() { + return Some(ExpressionKind::Literal(Literal::Slice(literal))); } if let Some(kind) = self.parse_block() { @@ -553,11 +617,13 @@ impl<'a> Parser<'a> { None } - fn parse_array_expression(&mut self) -> Option { - self.parse_array_literal() - .map(|array_literal| ExpressionKind::Literal(Literal::Array(array_literal))) - } - + /// ArrayLiteralExpression + /// = StandardArrayLiteralExpression + /// | RepeatedArrayLiteralExpression + /// + /// StandardArrayLiteralExpression = '[' (Expression ','?)* ']' + /// + /// RepeatedArrayLiteralExpression = '[' Expression ';' TypeExpression ']' fn parse_array_literal(&mut self) -> Option { if !self.eat_left_bracket() { return None; @@ -606,6 +672,32 @@ impl<'a> Parser<'a> { Some(ArrayLiteral::Standard(exprs)) } + /// SliceLiteralExpression + /// = StandardSliceLiteralExpression + /// | RepeatedSliceLiteralExpression + /// + /// StandardSliceLiteralExpression = '&' '[' (Expression ','?)* ']' + /// + /// RepeatedSliceLiteralExpression = '&' '[' Expression ';' TypeExpression ']' + fn parse_slice_literal(&mut self) -> Option { + if !self.tokens_follow(Token::Ampersand, Token::LeftBracket) { + return None; + } + + self.next_token(); + 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; @@ -644,6 +736,7 @@ impl<'a> Parser<'a> { }) } + /// Block = '{' (Statement ';'?)* '}' pub(super) fn parse_block(&mut self) -> Option { if !self.eat_left_brace() { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index c0357763db4..61169dfe88d 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -8,6 +8,8 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// EqualOrNotEqualExpression + /// = OrExpression (('==' | '!=') OrExpression)* pub(super) fn parse_equal_or_not_equal( &mut self, allow_constructors: bool, @@ -36,6 +38,8 @@ impl<'a> Parser<'a> { Some(lhs) } + /// OrExpression + /// = AndExpression ('|' AndExpression)* pub(super) fn parse_or(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_and(allow_constructors)?; @@ -64,6 +68,8 @@ impl<'a> Parser<'a> { Some(lhs) } + /// AndExpression + /// = XorExpression ('&' XorExpression)* pub(super) fn parse_and(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_xor(allow_constructors)?; @@ -92,6 +98,8 @@ impl<'a> Parser<'a> { Some(lhs) } + /// XorExpression + /// = LessOrGreaterExpression ('^' LessOrGreaterExpression)* pub(super) fn parse_xor(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_less_or_greater(allow_constructors)?; @@ -120,6 +128,8 @@ impl<'a> Parser<'a> { Some(lhs) } + /// LessOrGreaterExpression + /// = ShiftExpression (('<' | '<=' | '>' | '>=') ShiftExpression)* pub(super) fn parse_less_or_greater(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_shift(allow_constructors)?; @@ -150,6 +160,8 @@ impl<'a> Parser<'a> { Some(lhs) } + /// ShiftExpression + /// = AddOrSubtractExpression (('<<' | '>' '>') AddOrSubtractExpression)* pub(super) fn parse_shift(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_add_or_subtract(allow_constructors)?; @@ -180,6 +192,8 @@ impl<'a> Parser<'a> { Some(lhs) } + /// AddOrSubtractExpression + /// = MultiplyOrDivideOrModuloExpression (('+' | '-') MultiplyOrDivideOrModuloExpression)* pub(super) fn parse_add_or_subtract(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_multiply_or_divide_or_modulo(allow_constructors)?; @@ -210,6 +224,8 @@ impl<'a> Parser<'a> { Some(lhs) } + /// MultiplyOrDivideOrModuloExpression + /// = Term (('*' | '/' | '%') Term)* pub(super) fn parse_multiply_or_divide_or_modulo( &mut self, allow_constructors: bool, From 6b33cdd9ffe214343f8ea70ab29f022d2570abfb Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 09:18:54 -0300 Subject: [PATCH 193/229] Document attributes and arguments grammar --- compiler/noirc_frontend/src/parser/parser.rs | 2 +- .../noirc_frontend/src/parser/parser/{call.rs => arguments.rs} | 1 + compiler/noirc_frontend/src/parser/parser/attributes.rs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) rename compiler/noirc_frontend/src/parser/parser/{call.rs => arguments.rs} (94%) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index c6b8910b1a1..a630b41076f 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -10,8 +10,8 @@ use crate::{ use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReason}; +mod arguments; mod attributes; -mod call; mod doc_comments; mod expression; mod function; diff --git a/compiler/noirc_frontend/src/parser/parser/call.rs b/compiler/noirc_frontend/src/parser/parser/arguments.rs similarity index 94% rename from compiler/noirc_frontend/src/parser/parser/call.rs rename to compiler/noirc_frontend/src/parser/parser/arguments.rs index f83ffe22d4a..ae0772b5cd2 100644 --- a/compiler/noirc_frontend/src/parser/parser/call.rs +++ b/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -3,6 +3,7 @@ use crate::ast::Expression; use super::Parser; impl<'a> Parser<'a> { + /// Arguments = '(' (Expression ','?)* ')' pub(crate) fn parse_arguments(&mut self) -> Option> { if !self.eat_left_paren() { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 8312a6446c8..39a686d4d2c 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -7,6 +7,7 @@ use crate::token::{Attribute, Token, TokenKind}; use super::Parser; 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() { @@ -15,6 +16,7 @@ impl<'a> Parser<'a> { } } + /// Attributes = attribute* pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Span)> { let mut attributes = Vec::new(); From 5671c666b6aefa387671f85f0ec372bee78dff59 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 09:27:43 -0300 Subject: [PATCH 194/229] A bit more grammar --- compiler/noirc_frontend/src/parser/parser.rs | 2 + .../src/parser/parser/doc_comments.rs | 2 + .../noirc_frontend/src/parser/parser/item.rs | 13 ++++++ .../src/parser/parser/item_visibility.rs | 4 ++ .../src/parser/parser/lambda.rs | 4 ++ .../src/parser/parser/lambdas.rs | 42 ------------------- .../src/parser/parser/modifiers.rs | 1 + .../noirc_frontend/src/parser/parser/types.rs | 1 + 8 files changed, 27 insertions(+), 42 deletions(-) delete mode 100644 compiler/noirc_frontend/src/parser/parser/lambdas.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index a630b41076f..33e31790b61 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -124,12 +124,14 @@ impl<'a> Parser<'a> { parser } + /// Program = Module pub(crate) fn parse_program(&mut self) -> ParsedModule { self.parse_module( false, // nested ) } + /// 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_items(nested); diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index f8592cbe111..8dd55795964 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -3,6 +3,7 @@ use crate::token::{DocStyle, Token, TokenKind}; use super::Parser; impl<'a> Parser<'a> { + /// InnerDocComments = inner_doc_comment* pub(super) fn parse_inner_doc_comments(&mut self) -> Vec { let mut comments: Vec = Vec::new(); @@ -19,6 +20,7 @@ impl<'a> Parser<'a> { comments } + /// OuterDocComments = outer_doc_comments* pub(super) fn parse_outer_doc_comments(&mut self) -> Vec { let mut comments: Vec = Vec::new(); diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 74534867af1..489b7dc976a 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -57,6 +57,7 @@ impl<'a> Parser<'a> { items } + /// Item = OuterDocComments ItemKind fn parse_item(&mut self) -> Option { let start_span = self.current_token_span; let doc_comments = self.parse_outer_doc_comments(); @@ -66,6 +67,18 @@ impl<'a> Parser<'a> { 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)); diff --git a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 3bd1ab63975..1731284e354 100644 --- a/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -6,6 +6,10 @@ use crate::{ 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; diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index c1fb0d5937d..4def831a4d5 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -7,6 +7,10 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// Lambda = '|' (LambdaParameter ','?)* '|' Expression + /// + /// LambdaParameter + /// = Pattern OptionalTypeAnnotation pub(super) fn parse_lambda(&mut self) -> Option { if !self.eat_pipe() { return None; 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/modifiers.rs b/compiler/noirc_frontend/src/parser/parser/modifiers.rs index e74a3038bbf..543a639cdd2 100644 --- a/compiler/noirc_frontend/src/parser/parser/modifiers.rs +++ b/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -14,6 +14,7 @@ pub(crate) struct Modifiers { } impl<'a> Parser<'a> { + /// Modifiers = 'unconstrained'? ItemVisibility 'comptime'? 'mut'? pub(crate) fn parse_modifiers(&mut self, allow_mutable: bool) -> Modifiers { let unconstrained = self.parse_modifier(Keyword::Unconstrained); diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 048bac0d21c..ba7ee69f9cd 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -420,6 +420,7 @@ impl<'a> Parser<'a> { }) } + /// OptionalTypeAnnotation = (':' Type)? pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { if self.eat_colon() { self.parse_type_or_error() From 78d28cc49ba153a0866ae9e6a9779555248c90c0 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 09:54:40 -0300 Subject: [PATCH 195/229] More grammar --- .../src/parser/parser/function.rs | 9 ++ .../src/parser/parser/generics.rs | 17 ++++ .../src/parser/parser/global.rs | 1 + .../noirc_frontend/src/parser/parser/impls.rs | 94 ++++++++++++------- .../noirc_frontend/src/parser/parser/item.rs | 6 +- .../src/parser/parser/lambda.rs | 2 +- .../src/parser/parser/module.rs | 4 +- .../src/parser/parser/structs.rs | 3 + .../src/parser/parser/type_alias.rs | 1 + .../src/parser/parser/where_clause.rs | 3 + 10 files changed, 97 insertions(+), 43 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index a18b3ba9dad..87b55f53855 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -29,6 +29,7 @@ pub(crate) struct FunctionDefinitionWithOptionalBody { } impl<'a> Parser<'a> { + /// Function = 'fn' identifier Generics FunctionParameters ('->' Visibility Type) WhereClause (Block | ';') pub(crate) fn parse_function( &mut self, attributes: Vec<(Attribute, Span)>, @@ -123,6 +124,9 @@ impl<'a> Parser<'a> { } } + /// FunctionParameters = '(' (FunctionParameter ','?)* ')' + /// + /// FunctionParameter = Visibility PatternOrSelf ':' Type fn parse_function_parameters(&mut self, allow_self: bool) -> Vec { let mut parameters = Vec::new(); @@ -219,6 +223,11 @@ impl<'a> Parser<'a> { parameters } + /// Visibility + /// = 'pub' + /// | 'return_data' + /// | 'call_data' '(' int ')' + /// | nothing fn parse_visibility(&mut self) -> Visibility { if self.eat_keyword(Keyword::Pub) { return Visibility::Public; diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 307d6c95443..acefc95b5b3 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -12,6 +12,7 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// Generics = ('<' (Generic ','?)* '>')? pub(super) fn parse_generics(&mut self) -> UnresolvedGenerics { let mut generics = Vec::new(); @@ -47,6 +48,10 @@ impl<'a> Parser<'a> { generics } + /// Generic + /// = VariableGeneric + /// | NumericGeneric + /// | ResolvedGeneric fn parse_generic(&mut self) -> Option { if let Some(generic) = self.parse_variable_generic() { return Some(generic); @@ -63,6 +68,7 @@ impl<'a> Parser<'a> { None } + /// VariableGeneric = identifier fn parse_variable_generic(&mut self) -> Option { if let Some(ident) = self.eat_ident() { return Some(UnresolvedGeneric::Variable(ident)); @@ -71,6 +77,7 @@ impl<'a> Parser<'a> { None } + /// NumericGeneric = 'let' identifier ':' Type fn parse_numeric_generic(&mut self) -> Option { if !self.eat_keyword(Keyword::Let) { return None; @@ -100,6 +107,7 @@ impl<'a> Parser<'a> { Some(UnresolvedGeneric::Numeric { ident, typ }) } + /// ResolvedGeneric = quoted_type fn parse_resolved_generic(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::QuotedType) { match token.into_token() { @@ -113,6 +121,15 @@ impl<'a> Parser<'a> { None } + /// GenericTypeArgs = ('<' (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() { diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index c93f6c3632a..a10ad71a2c5 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -12,6 +12,7 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// Global = 'global' identifier OptionalTypeAnnotation '=' Expression ';' pub(crate) fn parse_global( &mut self, attributes: Vec<(Attribute, Span)>, diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index ae1189cf105..b8a7495dc2f 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -18,6 +18,9 @@ pub(crate) enum Impl { } impl<'a> Parser<'a> { + /// Impl + /// = TypeImpl + /// | TraitImpl pub(crate) fn parse_impl(&mut self) -> Impl { let generics = self.parse_generics(); @@ -43,13 +46,26 @@ impl<'a> Parser<'a> { }; } + 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_impl_body(); + let methods = self.parse_type_impl_body(); Impl::Impl(TypeImpl { object_type, type_span, generics, where_clause, methods }) } - fn parse_impl_body(&mut self) -> Vec<(Documented, Span)> { + /// TypeImplBody = '{' TypeImplItem* '}' + /// + /// TypeImplItem = OuterDocComments Attributes Modifiers Function + fn parse_type_impl_body(&mut self) -> Vec<(Documented, Span)> { let mut methods = Vec::new(); if !self.eat_left_brace() { @@ -97,6 +113,7 @@ impl<'a> Parser<'a> { methods } + /// TraitImpl = 'impl' Generics Path GenericTypeArgs 'for' Type TraitImplBody fn parse_trait_impl( &mut self, impl_generics: Vec, @@ -105,7 +122,7 @@ impl<'a> Parser<'a> { ) -> NoirTraitImpl { let object_type = self.parse_type_or_error(); let where_clause = self.parse_where_clause(); - let items = self.parse_trait_impl_items(); + let items = self.parse_trait_impl_body(); NoirTraitImpl { impl_generics, @@ -117,7 +134,8 @@ impl<'a> Parser<'a> { } } - fn parse_trait_impl_items(&mut self) -> Vec> { + /// TraitImplBody = '{' TraitImplItem* '}' + fn parse_trait_impl_body(&mut self) -> Vec> { let mut items = Vec::new(); if !self.eat_left_brace() { @@ -149,49 +167,23 @@ impl<'a> Parser<'a> { items } + /// 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_function() { - return Some(kind); - } - if let Some(kind) = self.parse_trait_impl_constant() { return Some(kind); } - None - } - - fn parse_trait_impl_function(&mut self) -> Option { - let modifiers = self.parse_modifiers( - false, // allow mut - ); - if modifiers.visibility != ItemVisibility::Private { - self.push_error( - ParserErrorReason::TraitImplVisibilityIgnored, - modifiers.visibility_span, - ); - } - let attributes = self.parse_attributes(); - - 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)) + 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; @@ -217,6 +209,7 @@ impl<'a> Parser<'a> { 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; @@ -243,6 +236,35 @@ impl<'a> Parser<'a> { 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)] diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 489b7dc976a..ea9afb1afa1 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -101,11 +101,7 @@ impl<'a> Parser<'a> { if let Some(is_contract) = self.eat_mod_or_contract() { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); - return Some(self.parse_module_or_contract( - attributes, - is_contract, - modifiers.visibility, - )); + return Some(self.parse_mod_or_contract(attributes, is_contract, modifiers.visibility)); } if self.eat_keyword(Keyword::Struct) { diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index 4def831a4d5..1d4697cb3da 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -7,7 +7,7 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - /// Lambda = '|' (LambdaParameter ','?)* '|' Expression + /// Lambda = '|' (LambdaParameter ','?)* '|' ('->' Type)? Expression /// /// LambdaParameter /// = Pattern OptionalTypeAnnotation diff --git a/compiler/noirc_frontend/src/parser/parser/module.rs b/compiler/noirc_frontend/src/parser/parser/module.rs index a1bf24dd22c..0ad68d8ed2e 100644 --- a/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -9,7 +9,9 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - pub(super) fn parse_module_or_contract( + /// ModOrContract + /// = ('mod' | 'contract') identifier ('{' Module '}' | ';') + pub(super) fn parse_mod_or_contract( &mut self, attributes: Vec<(Attribute, Span)>, is_contract: bool, diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 493e6bd2d50..9a1fb32dc54 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -9,6 +9,9 @@ use crate::{ use super::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)>, diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index d652a58d881..05b436b59b5 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -8,6 +8,7 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// TypeAlias = 'type' identifier Generics '=' Type ';' pub(crate) fn parse_type_alias( &mut self, visibility: ItemVisibility, diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index c4b480832a5..bd46d7e6bb1 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -7,6 +7,7 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// WhereClause = 'where' (Type ':' TraitBounds ','?)* pub(super) fn parse_where_clause(&mut self) -> Vec { let mut constraints = Vec::new(); @@ -40,6 +41,7 @@ impl<'a> Parser<'a> { constraints } + /// TraitBounds = (TraitBound '+'?)* pub(super) fn parse_trait_bounds(&mut self) -> Vec { let mut bounds = Vec::new(); @@ -79,6 +81,7 @@ impl<'a> Parser<'a> { } } + /// 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(); From d02a5312ead2ff0fedf5def3f647d3e92ab29fe8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 10:04:48 -0300 Subject: [PATCH 196/229] More grammar --- .../noirc_frontend/src/parser/parser/path.rs | 12 ++ .../src/parser/parser/pattern.rs | 43 +++-- .../src/parser/parser/primitives.rs | 167 ------------------ 3 files changed, 42 insertions(+), 180 deletions(-) delete mode 100644 compiler/noirc_frontend/src/parser/parser/primitives.rs diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 23fee65cfe9..70f7a35753e 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -27,6 +27,10 @@ impl<'a> Parser<'a> { /// 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 @@ -48,6 +52,7 @@ impl<'a> Parser<'a> { } } + /// PathNoTurbofish = PathKind identifier ('::' identifier)* pub fn parse_path_no_turbofish(&mut self) -> Option { self.parse_path_impl( false, // allow turbofish @@ -151,6 +156,7 @@ impl<'a> Parser<'a> { Path { segments, kind, span: self.span_since(start_span) } } + /// PathGenerics = GenericTypeArgs pub(super) fn parse_path_generics( &mut self, on_named_arg_error: ParserErrorReason, @@ -167,6 +173,11 @@ impl<'a> Parser<'a> { Some(generics.ordered_args) } + /// PathKind + /// | 'crate' '::' + /// | 'dep' '::' + /// | 'super' '::' + /// | nothing pub(super) fn parse_path_kind(&mut self) -> PathKind { if self.eat_keyword(Keyword::Crate) { PathKind::Crate @@ -179,6 +190,7 @@ impl<'a> Parser<'a> { } } + /// AsTraitPath = '<' Type 'as' PathNoTurbofish GenericTypeArgs '>' '::' identifier pub(super) fn parse_as_trait_path(&mut self) -> Option { if !self.eat_less() { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index b74f26b9487..f379d66c947 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -29,12 +29,17 @@ impl<'a> Parser<'a> { 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; @@ -101,6 +106,13 @@ impl<'a> Parser<'a> { }) } + /// PatternNoMut + /// = InternedPattern + /// | TuplePattern + /// | StructPattern + /// | IdentifierPattern + /// + /// IdentifierPattern = identifier fn parse_pattern_no_mut(&mut self) -> Option { if let Some(pattern) = self.parse_interned_pattern() { return Some(pattern); @@ -135,6 +147,21 @@ impl<'a> Parser<'a> { 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 = '(' (Pattern ','?)* ')' fn parse_tuple_pattern(&mut self) -> Option { let start_span = self.current_token_span; @@ -168,6 +195,9 @@ impl<'a> Parser<'a> { Some(Pattern::Tuple(patterns, self.span_since(start_span))) } + /// StructPattern = Path '{' (StructPatternField ','?)* '}' + /// + /// StructPatternField = identifier (':' Pattern)? fn parse_struct_pattern(&mut self, path: Path) -> Pattern { let start_span = path.span(); @@ -203,19 +233,6 @@ impl<'a> Parser<'a> { Pattern::Struct(path, patterns, self.span_since(start_span)) } - 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!(), - } - } - fn at_built_in_type(&self) -> bool { matches!( self.token.token(), 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"], - ); - } -} From b0c67505a40decdcdd0f9232b4e8abc3931426f0 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 10:14:29 -0300 Subject: [PATCH 197/229] A bit more grammar --- .../src/parser/parser/expression.rs | 2 +- .../src/parser/parser/statement.rs | 62 ++++++++++++++++--- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 4611aabdf00..f52b715a32e 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -736,7 +736,7 @@ impl<'a> Parser<'a> { }) } - /// Block = '{' (Statement ';'?)* '}' + /// Block = '{' Statement* '}' pub(super) fn parse_block(&mut self) -> Option { if !self.eat_left_brace() { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index b5a62467328..beebde6855c 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -22,6 +22,7 @@ impl<'a> Parser<'a> { } } + /// Statement = Attributes StatementKind ';'? pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { loop { let attributes = self.parse_attributes(); @@ -55,6 +56,30 @@ impl<'a> Parser<'a> { } } + /// StatementKind + /// = BreakStatement + /// | ContinueStatement + /// | ReturnStatement + /// | LetStatement + /// | ConstrainStatement + /// | ComptimeStatement + /// | ForStatement + /// | IfStatement + /// | BlockStatement + /// | AssignStatement + /// | ExpressionStatement + /// + /// BreakStatement = 'break' + /// + /// ContinueStatement = 'continue' + /// + /// ReturnStatement = 'return' Expression? + /// + /// BlockStatement = Block + /// + /// AssignStatement = Expression '=' Expression + /// + /// ExpressionStatement = Expression fn parse_statement_kind( &mut self, attributes: Vec<(Attribute, Span)>, @@ -197,6 +222,7 @@ impl<'a> Parser<'a> { } } + /// ForStatement = 'for' identifier 'in' ForRange Block fn parse_for(&mut self) -> Option { let start_span = self.current_token_span; @@ -215,13 +241,7 @@ impl<'a> Parser<'a> { return Some(self.empty_for_loop(identifier, start_span)); } - let expr = self.parse_expression_no_constructors_or_error(); - - let range = if self.eat(Token::DoubleDot) { - ForRange::Range(expr, self.parse_expression_no_constructors_or_error()) - } else { - ForRange::Array(expr) - }; + let range = self.parse_for_range(); let block_start_span = self.current_token_span; let block = if let Some(block) = self.parse_block() { @@ -237,6 +257,19 @@ impl<'a> Parser<'a> { Some(ForLoopStatement { identifier, range, block, span: self.span_since(start_span) }) } + /// ForRange + /// = Expression + /// | Expression '..' Expression + fn parse_for_range(&mut self) -> ForRange { + let expr = self.parse_expression_no_constructors_or_error(); + + if self.eat(Token::DoubleDot) { + ForRange::Range(expr, self.parse_expression_no_constructors_or_error()) + } else { + ForRange::Array(expr) + } + } + fn empty_for_loop(&mut self, identifier: Ident, start_span: Span) -> ForLoopStatement { ForLoopStatement { identifier, @@ -249,6 +282,16 @@ impl<'a> Parser<'a> { } } + /// ComptimeStatement + /// = ComptimeBlock + /// | ComptimeLet + /// | ComptimeFor + /// + /// ComptimeBlock = 'comptime' Block + /// + /// ComptimeLet = 'comptime' LetStatement + /// + /// ComptimeFor = 'comptime' ForStatement fn parse_comptime_statement( &mut self, attributes: Vec<(Attribute, Span)>, @@ -299,6 +342,7 @@ impl<'a> Parser<'a> { 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; @@ -317,6 +361,10 @@ impl<'a> Parser<'a> { 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 { From 45ad22231eee2c941f8668549358b87ce7332144 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 10:17:55 -0300 Subject: [PATCH 198/229] A bit more --- compiler/noirc_frontend/src/parser/parser/traits.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 8597d09447e..ecd428d0433 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -10,6 +10,7 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// Trait = 'trait' identifier Generics WhereClause TraitBody pub(crate) fn parse_trait( &mut self, attributes: Vec<(Attribute, Span)>, @@ -25,7 +26,7 @@ impl<'a> Parser<'a> { let generics = self.parse_generics(); let where_clause = self.parse_where_clause(); - let items = self.parse_trait_items(); + let items = self.parse_trait_body(); NoirTrait { name, @@ -38,7 +39,8 @@ impl<'a> Parser<'a> { } } - fn parse_trait_items(&mut self) -> Vec> { + /// TraitBody = '{' (OuterDocComments TraitItem)* '}' + fn parse_trait_body(&mut self) -> Vec> { let mut items = Vec::new(); if !self.eat_left_brace() { @@ -72,6 +74,10 @@ impl<'a> Parser<'a> { items } + /// TraitItem + /// = TraitType + /// | TraitConstant + /// | TraitFunction fn parse_trait_item(&mut self) -> Option { if let Some(item) = self.parse_trait_type() { return Some(item); @@ -88,6 +94,7 @@ impl<'a> Parser<'a> { None } + /// TraitType = 'type' identifier ';' fn parse_trait_type(&mut self) -> Option { if !self.eat_keyword(Keyword::Type) { return None; @@ -106,6 +113,7 @@ impl<'a> Parser<'a> { Some(TraitItem::Type { name }) } + /// TraitConstant = 'let' identifier ':' Type ('=' Expression) ';' fn parse_trait_constant(&mut self) -> Option { if !self.eat_keyword(Keyword::Let) { return None; @@ -134,6 +142,7 @@ impl<'a> Parser<'a> { 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 From bcc666377186851266e64a746faedbab5a66d0c5 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 10:24:50 -0300 Subject: [PATCH 199/229] Finalize the grammar --- .../src/parser/parser/type_expression.rs | 16 ++++++++++++++++ .../noirc_frontend/src/parser/parser/use_tree.rs | 3 +++ 2 files changed, 19 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 5d2271a201e..a355887b2f4 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -14,6 +14,7 @@ use noirc_errors::Span; use super::Parser; impl<'a> Parser<'a> { + /// TypeExpression= AddOrSubtractTypeExpression pub(crate) fn parse_type_expression( &mut self, ) -> Result { @@ -23,6 +24,8 @@ impl<'a> Parser<'a> { } } + /// 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()?; @@ -62,6 +65,8 @@ impl<'a> Parser<'a> { lhs } + /// MultiplyOrDivideOrModuloTypeExpression + /// = TermTypeExpression (('*' | '/' | '%') TermTypeExpression)* fn parse_multiply_or_divide_or_modulo_type_expression( &mut self, ) -> Option { @@ -106,6 +111,9 @@ impl<'a> Parser<'a> { lhs } + /// TermTypeExpression + /// = '- TermTypeExpression + /// | AtomTypeExpression fn parse_term_type_expression(&mut self) -> Option { let start_span = self.current_token_span; if self.eat(Token::Minus) { @@ -131,6 +139,10 @@ impl<'a> Parser<'a> { 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); @@ -147,6 +159,7 @@ impl<'a> Parser<'a> { None } + /// ConstantTypeExpression = int fn parse_constant_type_expression(&mut self) -> Option { let Some(int) = self.eat_int() else { return None; @@ -169,11 +182,13 @@ impl<'a> Parser<'a> { 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) { @@ -194,6 +209,7 @@ impl<'a> Parser<'a> { } } + /// 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; diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index acd7f39dd96..003db979501 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -9,6 +9,9 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { + /// Use = 'use' PathKind PathNoTurbofish UseTree + /// + /// UseTree = PathNoTurbofish ('::' '{' (UseTree ',')? '}')? pub(super) fn parse_use_tree(&mut self) -> UseTree { let start_span = self.current_token_span; From 97c2ba0bd422be9fc8e907ea389c4195a6e29e40 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 16:06:06 -0300 Subject: [PATCH 200/229] Small fix to grammar --- compiler/noirc_frontend/src/parser/parser/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 87b55f53855..d5cb9afccf6 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -29,7 +29,7 @@ pub(crate) struct FunctionDefinitionWithOptionalBody { } impl<'a> Parser<'a> { - /// Function = 'fn' identifier Generics FunctionParameters ('->' Visibility Type) WhereClause (Block | ';') + /// Function = 'fn' identifier Generics FunctionParameters ('->' Visibility Type)? WhereClause (Block | ';') pub(crate) fn parse_function( &mut self, attributes: Vec<(Attribute, Span)>, From 2884e3e3e19bce5d4a61e88dec5dc779a83a2654 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 17:23:12 -0300 Subject: [PATCH 201/229] Improve a bit the grammar --- compiler/noirc_frontend/src/parser/parser/expression.rs | 6 +++++- compiler/noirc_frontend/src/parser/parser/function.rs | 2 +- compiler/noirc_frontend/src/parser/parser/lambda.rs | 4 +++- compiler/noirc_frontend/src/parser/parser/pattern.rs | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index f52b715a32e..25b03341292 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -404,7 +404,11 @@ impl<'a> Parser<'a> { Some(ExpressionKind::Variable(path)) } - /// ConstructorExpression = Type '{' (identifier (':' Expression)?)* '}' + /// ConstructorExpression = Type '{' ConstructorFields? '}' + /// + /// ConstructorFields = ConstructorField ( ',' ConstructorField )* ','? + /// + /// ConstructorField = identifier (':' Expression)? fn parse_constructor(&mut self, typ: UnresolvedType) -> ExpressionKind { let mut fields = Vec::new(); let mut trailing_comma = false; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index d5cb9afccf6..5951d068ec2 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -124,7 +124,7 @@ impl<'a> Parser<'a> { } } - /// FunctionParameters = '(' (FunctionParameter ','?)* ')' + /// FunctionParameters = '(' (FunctionParameter (',' FunctionParameter)* ','? )? ')' /// /// FunctionParameter = Visibility PatternOrSelf ':' Type fn parse_function_parameters(&mut self, allow_self: bool) -> Vec { diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index 1d4697cb3da..d16a2da76b4 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -7,7 +7,9 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - /// Lambda = '|' (LambdaParameter ','?)* '|' ('->' Type)? Expression + /// Lambda = '|' LambdaParameters? '|' ('->' Type)? Expression + /// + /// LambdaParameters = LambdaParameter (',' LambdaParameter)? ','? /// /// LambdaParameter /// = Pattern OptionalTypeAnnotation diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index f379d66c947..b330f0b9950 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -195,7 +195,9 @@ impl<'a> Parser<'a> { Some(Pattern::Tuple(patterns, self.span_since(start_span))) } - /// StructPattern = Path '{' (StructPatternField ','?)* '}' + /// StructPattern = Path '{' StructPatternFields? '}' + /// + /// StructPatternFields = StructPatternField (',' StructPatternField)? ','? /// /// StructPatternField = identifier (':' Pattern)? fn parse_struct_pattern(&mut self, path: Path) -> Pattern { From 01d921e7960b7bba02fdf63b4890e9ef34a0bcb2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 17:44:04 -0300 Subject: [PATCH 202/229] Improve the grammar a bit more --- .../src/parser/parser/arguments.rs | 4 ++- .../src/parser/parser/expression.rs | 34 +++++++++---------- .../src/parser/parser/function.rs | 6 ++-- .../src/parser/parser/generics.rs | 8 +++-- .../noirc_frontend/src/parser/parser/impls.rs | 2 +- .../noirc_frontend/src/parser/parser/infix.rs | 16 ++++----- .../src/parser/parser/lambda.rs | 4 +-- .../src/parser/parser/module.rs | 2 +- .../noirc_frontend/src/parser/parser/path.rs | 4 +-- .../src/parser/parser/pattern.rs | 8 +++-- .../src/parser/parser/traits.rs | 4 +-- .../src/parser/parser/type_expression.rs | 4 +-- .../noirc_frontend/src/parser/parser/types.rs | 2 +- .../src/parser/parser/use_tree.rs | 4 ++- .../src/parser/parser/where_clause.rs | 8 +++-- 15 files changed, 62 insertions(+), 48 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/arguments.rs b/compiler/noirc_frontend/src/parser/parser/arguments.rs index ae0772b5cd2..b41309c847b 100644 --- a/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -3,7 +3,9 @@ use crate::ast::Expression; use super::Parser; impl<'a> Parser<'a> { - /// Arguments = '(' (Expression ','?)* ')' + /// Arguments = '(' ArgumentsList? ')' + /// + /// ArgumentsList = Expression ( ',' Expression )? ','? pub(crate) fn parse_arguments(&mut self) -> Option> { if !self.eat_left_paren() { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 25b03341292..bbecf8048a9 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -408,7 +408,7 @@ impl<'a> Parser<'a> { /// /// ConstructorFields = ConstructorField ( ',' ConstructorField )* ','? /// - /// ConstructorField = identifier (':' Expression)? + /// ConstructorField = identifier ( ':' Expression )? fn parse_constructor(&mut self, typ: UnresolvedType) -> ExpressionKind { let mut fields = Vec::new(); let mut trailing_comma = false; @@ -445,7 +445,7 @@ impl<'a> Parser<'a> { })) } - /// IfExpression = 'if' Expression Block ('else' (Block | IfExpression))? + /// IfExpression = 'if' Expression Block ( 'else' ( Block | IfExpression ) )? pub(super) fn parse_if_expr(&mut self) -> Option { if !self.eat_keyword(Keyword::If) { return None; @@ -537,7 +537,7 @@ impl<'a> Parser<'a> { None } - /// TypePathExpression = PrimitiveType '::' identifier ('::' GenericTypeArgs)? + /// 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 { @@ -574,12 +574,14 @@ impl<'a> Parser<'a> { /// | rawstr /// | fmtstr /// | QuoteExpression - /// | ArrayLiteralExpression - /// | SliceLiteralExpression + /// | ArrayExpression + /// | SliceExpression /// | BlockExpression /// /// QuoteExpression = 'quote' '{' token* '}' /// + /// ArrayExpression = ArrayLiteral + /// /// BlockExpression = Block fn parse_literal(&mut self) -> Option { if let Some(bool) = self.eat_bool() { @@ -621,13 +623,15 @@ impl<'a> Parser<'a> { None } - /// ArrayLiteralExpression - /// = StandardArrayLiteralExpression - /// | RepeatedArrayLiteralExpression + /// ArrayLiteral + /// = StandardArrayLiteral + /// | RepeatedArrayLiteral + /// + /// StandardArrayLiteral = '[' ArrayElements? ']' /// - /// StandardArrayLiteralExpression = '[' (Expression ','?)* ']' + /// ArrayElements = Expression ( ',' Expression )? ','? /// - /// RepeatedArrayLiteralExpression = '[' Expression ';' TypeExpression ']' + /// RepeatedArrayLiteral = '[' Expression ';' TypeExpression ']' fn parse_array_literal(&mut self) -> Option { if !self.eat_left_bracket() { return None; @@ -676,13 +680,7 @@ impl<'a> Parser<'a> { Some(ArrayLiteral::Standard(exprs)) } - /// SliceLiteralExpression - /// = StandardSliceLiteralExpression - /// | RepeatedSliceLiteralExpression - /// - /// StandardSliceLiteralExpression = '&' '[' (Expression ','?)* ']' - /// - /// RepeatedSliceLiteralExpression = '&' '[' Expression ';' TypeExpression ']' + /// SliceExpression = '&' ArrayLiteral fn parse_slice_literal(&mut self) -> Option { if !self.tokens_follow(Token::Ampersand, Token::LeftBracket) { return None; @@ -701,7 +699,7 @@ impl<'a> Parser<'a> { /// /// ParenthesizedExpression = '(' Expression ')' /// - /// TupleExpression = '(' Expression (',' Expression)+ ')' + /// TupleExpression = '(' Expression ( ',' Expression )+ ','? ')' fn parse_parentheses_expression(&mut self) -> Option { if !self.eat_left_paren() { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 5951d068ec2..7180ca67a90 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -29,7 +29,7 @@ pub(crate) struct FunctionDefinitionWithOptionalBody { } impl<'a> Parser<'a> { - /// Function = 'fn' identifier Generics FunctionParameters ('->' Visibility Type)? WhereClause (Block | ';') + /// Function = 'fn' identifier Generics FunctionParameters ( '->' Visibility Type )? WhereClause ( Block | ';' ) pub(crate) fn parse_function( &mut self, attributes: Vec<(Attribute, Span)>, @@ -124,7 +124,9 @@ impl<'a> Parser<'a> { } } - /// FunctionParameters = '(' (FunctionParameter (',' FunctionParameter)* ','? )? ')' + /// FunctionParameters = '(' FunctionParametersList? ')' + /// + /// FunctionParametersList = FunctionParameter ( ',' FunctionParameter )* ','? /// /// FunctionParameter = Visibility PatternOrSelf ':' Type fn parse_function_parameters(&mut self, allow_self: bool) -> Vec { diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index acefc95b5b3..2a6888ceb3a 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -12,7 +12,9 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - /// Generics = ('<' (Generic ','?)* '>')? + /// Generics = ( '<' GenericsList? '>' )? + /// + /// GenericsList = Generic ( ',' Generic )* ','? pub(super) fn parse_generics(&mut self) -> UnresolvedGenerics { let mut generics = Vec::new(); @@ -121,7 +123,9 @@ impl<'a> Parser<'a> { None } - /// GenericTypeArgs = ('<' (GenericTypeArg ','?)* '>') + /// GenericTypeArgs = ( '<' GenericTypeArgsList? '>' ) + /// + /// GenericTypeArgsList = GenericTypeArg ( ',' GenericTypeArg )* ','? /// /// GenericTypeArg /// = NamedTypeArg diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index b8a7495dc2f..05d7ff4194d 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -183,7 +183,7 @@ impl<'a> Parser<'a> { self.parse_trait_impl_function() } - /// TraitImplType = 'type' identifier (':' Type)? ';' + /// TraitImplType = 'type' identifier ( ':' Type )? ';' fn parse_trait_impl_type(&mut self) -> Option { if !self.eat_keyword(Keyword::Type) { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 61169dfe88d..b5608d202d6 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -9,7 +9,7 @@ use super::Parser; impl<'a> Parser<'a> { /// EqualOrNotEqualExpression - /// = OrExpression (('==' | '!=') OrExpression)* + /// = OrExpression ( ( '==' | '!=' ) OrExpression )* pub(super) fn parse_equal_or_not_equal( &mut self, allow_constructors: bool, @@ -39,7 +39,7 @@ impl<'a> Parser<'a> { } /// OrExpression - /// = AndExpression ('|' AndExpression)* + /// = AndExpression ( '|' AndExpression )* pub(super) fn parse_or(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_and(allow_constructors)?; @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { } /// AndExpression - /// = XorExpression ('&' XorExpression)* + /// = XorExpression ( '&' XorExpression )* pub(super) fn parse_and(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_xor(allow_constructors)?; @@ -99,7 +99,7 @@ impl<'a> Parser<'a> { } /// XorExpression - /// = LessOrGreaterExpression ('^' LessOrGreaterExpression)* + /// = LessOrGreaterExpression ( '^' LessOrGreaterExpression )* pub(super) fn parse_xor(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_less_or_greater(allow_constructors)?; @@ -129,7 +129,7 @@ impl<'a> Parser<'a> { } /// LessOrGreaterExpression - /// = ShiftExpression (('<' | '<=' | '>' | '>=') ShiftExpression)* + /// = ShiftExpression ( ( '<' | '<=' | '>' | '>=' ) ShiftExpression )* pub(super) fn parse_less_or_greater(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_shift(allow_constructors)?; @@ -161,7 +161,7 @@ impl<'a> Parser<'a> { } /// ShiftExpression - /// = AddOrSubtractExpression (('<<' | '>' '>') AddOrSubtractExpression)* + /// = AddOrSubtractExpression ( ( '<<' | '>' '>' ) AddOrSubtractExpression )* pub(super) fn parse_shift(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_add_or_subtract(allow_constructors)?; @@ -193,7 +193,7 @@ impl<'a> Parser<'a> { } /// AddOrSubtractExpression - /// = MultiplyOrDivideOrModuloExpression (('+' | '-') MultiplyOrDivideOrModuloExpression)* + /// = MultiplyOrDivideOrModuloExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloExpression )* pub(super) fn parse_add_or_subtract(&mut self, allow_constructors: bool) -> Option { let start_span = self.current_token_span; let mut lhs = self.parse_multiply_or_divide_or_modulo(allow_constructors)?; @@ -225,7 +225,7 @@ impl<'a> Parser<'a> { } /// MultiplyOrDivideOrModuloExpression - /// = Term (('*' | '/' | '%') Term)* + /// = Term ( ( '*' | '/' | '%' ) Term )* pub(super) fn parse_multiply_or_divide_or_modulo( &mut self, allow_constructors: bool, diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index d16a2da76b4..6c83dd633a1 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -7,9 +7,9 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - /// Lambda = '|' LambdaParameters? '|' ('->' Type)? Expression + /// Lambda = '|' LambdaParameters? '|' ( '->' Type )? Expression /// - /// LambdaParameters = LambdaParameter (',' LambdaParameter)? ','? + /// LambdaParameters = LambdaParameter ( ',' LambdaParameter )? ','? /// /// LambdaParameter /// = Pattern OptionalTypeAnnotation diff --git a/compiler/noirc_frontend/src/parser/parser/module.rs b/compiler/noirc_frontend/src/parser/parser/module.rs index 0ad68d8ed2e..263338863c0 100644 --- a/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/compiler/noirc_frontend/src/parser/parser/module.rs @@ -10,7 +10,7 @@ use super::Parser; impl<'a> Parser<'a> { /// ModOrContract - /// = ('mod' | 'contract') identifier ('{' Module '}' | ';') + /// = ( 'mod' | 'contract' ) identifier ( '{' Module '}' | ';' ) pub(super) fn parse_mod_or_contract( &mut self, attributes: Vec<(Attribute, Span)>, diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 70f7a35753e..ea900e46e1b 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { /// Tries to parse a Path. /// Note that `crate::`, `super::`, etc., are not valid paths on their own. /// - /// Path = PathKind identifier Turbofish? ('::' identifier Turbofish?)* + /// Path = PathKind identifier Turbofish? ( '::' identifier Turbofish? )* /// /// Turbofish = '::' PathGenerics pub(crate) fn parse_path(&mut self) -> Option { @@ -52,7 +52,7 @@ impl<'a> Parser<'a> { } } - /// PathNoTurbofish = PathKind identifier ('::' identifier)* + /// PathNoTurbofish = PathKind identifier ( '::' identifier )* pub fn parse_path_no_turbofish(&mut self) -> Option { self.parse_path_impl( false, // allow turbofish diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index b330f0b9950..dbdf059c678 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -161,7 +161,9 @@ impl<'a> Parser<'a> { } } - /// TuplePattern = '(' (Pattern ','?)* ')' + /// TuplePattern = '(' PatternList? ')' + /// + /// PatternList = Pattern ( ',' Pattern )* ','? fn parse_tuple_pattern(&mut self) -> Option { let start_span = self.current_token_span; @@ -197,9 +199,9 @@ impl<'a> Parser<'a> { /// StructPattern = Path '{' StructPatternFields? '}' /// - /// StructPatternFields = StructPatternField (',' StructPatternField)? ','? + /// StructPatternFields = StructPatternField ( ',' StructPatternField )? ','? /// - /// StructPatternField = identifier (':' Pattern)? + /// StructPatternField = identifier ( ':' Pattern )? fn parse_struct_pattern(&mut self, path: Path) -> Pattern { let start_span = path.span(); diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index ecd428d0433..8c0cc4e1d1e 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -39,7 +39,7 @@ impl<'a> Parser<'a> { } } - /// TraitBody = '{' (OuterDocComments TraitItem)* '}' + /// TraitBody = '{' ( OuterDocComments TraitItem )* '}' fn parse_trait_body(&mut self) -> Vec> { let mut items = Vec::new(); @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { Some(TraitItem::Type { name }) } - /// TraitConstant = 'let' identifier ':' Type ('=' Expression) ';' + /// TraitConstant = 'let' identifier ':' Type ( '=' Expression ) ';' fn parse_trait_constant(&mut self) -> Option { if !self.eat_keyword(Keyword::Let) { return None; diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index a355887b2f4..5635be696a0 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -25,7 +25,7 @@ impl<'a> Parser<'a> { } /// AddOrSubtractTypeExpression - /// = MultiplyOrDivideOrModuloTypeExpression (('+' | '-') MultiplyOrDivideOrModuloTypeExpression)* + /// = 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()?; @@ -66,7 +66,7 @@ impl<'a> Parser<'a> { } /// MultiplyOrDivideOrModuloTypeExpression - /// = TermTypeExpression (('*' | '/' | '%') TermTypeExpression)* + /// = TermTypeExpression ( ( '*' | '/' | '%' ) TermTypeExpression )* fn parse_multiply_or_divide_or_modulo_type_expression( &mut self, ) -> Option { diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index ba7ee69f9cd..ccde29d2cc7 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -420,7 +420,7 @@ impl<'a> Parser<'a> { }) } - /// OptionalTypeAnnotation = (':' Type)? + /// OptionalTypeAnnotation = ( ':' Type )? pub(super) fn parse_optional_type_annotation(&mut self) -> UnresolvedType { if self.eat_colon() { self.parse_type_or_error() diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 003db979501..e8d65f0f93d 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -11,7 +11,9 @@ use super::Parser; impl<'a> Parser<'a> { /// Use = 'use' PathKind PathNoTurbofish UseTree /// - /// UseTree = PathNoTurbofish ('::' '{' (UseTree ',')? '}')? + /// UseTree = PathNoTurbofish ( '::' '{' UseTreeList? '}' )? + /// + /// UseTreeList = UseTree (',' UseTree)* ','? pub(super) fn parse_use_tree(&mut self) -> UseTree { let start_span = self.current_token_span; diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index bd46d7e6bb1..4ef81afdc7f 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -7,7 +7,11 @@ use crate::{ use super::Parser; impl<'a> Parser<'a> { - /// WhereClause = 'where' (Type ':' TraitBounds ','?)* + /// WhereClause = 'where' WhereClauseItems? + /// + /// WhereClauseItems = WhereClauseItem ( ',' WhereClauseItem )* ','? + /// + /// WhereClauseItem = Type ':' TraitBounds pub(super) fn parse_where_clause(&mut self) -> Vec { let mut constraints = Vec::new(); @@ -41,7 +45,7 @@ impl<'a> Parser<'a> { constraints } - /// TraitBounds = (TraitBound '+'?)* + /// TraitBounds = TraitBound ( '+' TraitBound )? '+'? pub(super) fn parse_trait_bounds(&mut self) -> Vec { let mut bounds = Vec::new(); From 109bed65cf7343d8c8b91711a7b98878df2c8016 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 2 Oct 2024 17:47:22 -0300 Subject: [PATCH 203/229] Add a few more comments --- .../src/parser/parser/type_expression.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 5635be696a0..6c2cc670057 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -213,6 +213,9 @@ impl<'a> Parser<'a> { 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 @@ -230,6 +233,8 @@ impl<'a> Parser<'a> { 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); } @@ -244,6 +249,8 @@ impl<'a> Parser<'a> { ) -> 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); } @@ -257,6 +264,7 @@ impl<'a> Parser<'a> { 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); @@ -323,6 +331,8 @@ impl<'a> Parser<'a> { 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) let mut typ_span = typ.span; if let UnresolvedTypeData::Expression(type_expr) = typ.typ { self.eat_or_error(Token::RightParen); From 3322f816630e2309445e4874d771a8e49699968d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 3 Oct 2024 07:08:14 -0300 Subject: [PATCH 204/229] Refactor and introduce CallArguments --- .../src/parser/parser/arguments.rs | 20 ++++++++++++- .../src/parser/parser/expression.rs | 28 ++++++------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/arguments.rs b/compiler/noirc_frontend/src/parser/parser/arguments.rs index b41309c847b..738a4f31778 100644 --- a/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -1,7 +1,12 @@ -use crate::ast::Expression; +use crate::{ast::Expression, token::Token}; use super::Parser; +pub(crate) struct CallArguments { + pub(crate) arguments: Vec, + pub(crate) is_macro_call: bool, +} + impl<'a> Parser<'a> { /// Arguments = '(' ArgumentsList? ')' /// @@ -35,4 +40,17 @@ impl<'a> Parser<'a> { Some(arguments) } + + /// CallArguments = '!'? Arguments + pub(super) fn parse_call_arguments(&mut self) -> Option { + let is_macro_call = self.tokens_follow(Token::Bang, 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.next_token(); + } + + self.parse_arguments().map(|arguments| CallArguments { arguments, is_macro_call }) + } } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index bbecf8048a9..d9edd3100ba 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -127,19 +127,13 @@ impl<'a> Parser<'a> { self.parse_index(atom, start_span) } - /// CallExpression = Atom '!'? Arguments + /// CallExpression = Atom CallArguments fn parse_call(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { - let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); - if is_macro_call { - // Next `self.parse_arguments` will return `Some(...)` - self.next_token(); - } - - if let Some(arguments) = self.parse_arguments() { + if let Some(call_arguments) = self.parse_call_arguments() { let kind = ExpressionKind::Call(Box::new(CallExpression { func: Box::new(atom), - arguments, - is_macro_call, + arguments: call_arguments.arguments, + is_macro_call: call_arguments.is_macro_call, })); let span = self.span_since(start_span); let atom = Expression { kind, span }; @@ -155,7 +149,7 @@ impl<'a> Parser<'a> { /// /// MemberAccessExpression = Atom '.' identifier /// - /// MethodCallExpression = Atom '.' identifier '!'? Arguments + /// MethodCallExpression = Atom '.' identifier CallArguments fn parse_member_access_or_method_call( &mut self, atom: Expression, @@ -169,19 +163,13 @@ impl<'a> Parser<'a> { let generics = self.parse_generics_after_member_access_field_name(); - let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); - if is_macro_call { - // Next `self.parse_arguments` will return `Some(...)` - self.next_token(); - } - - let kind = if let Some(arguments) = self.parse_arguments() { + let kind = if let Some(call_arguments) = self.parse_call_arguments() { ExpressionKind::MethodCall(Box::new(MethodCallExpression { object: atom, method_name: field_name, generics, - arguments, - is_macro_call, + arguments: call_arguments.arguments, + is_macro_call: call_arguments.is_macro_call, })) } else { ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { From 6bbf96444df21c70114562937dfaccedcb09457b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 3 Oct 2024 07:52:29 -0300 Subject: [PATCH 205/229] Reduce duplication when parsing infix expressions --- .../noirc_frontend/src/parser/parser/infix.rs | 281 ++++++------------ 1 file changed, 99 insertions(+), 182 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index b5608d202d6..08b36e17c6a 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -14,214 +14,116 @@ impl<'a> Parser<'a> { &mut self, allow_constructors: bool, ) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_or(allow_constructors)?; - - loop { - let operator = if self.eat(Token::Equal) { - BinaryOpKind::Equal - } else if self.eat(Token::NotEqual) { - BinaryOpKind::NotEqual + 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 { - break; - }; - let operator = Spanned::from(self.previous_token_span, operator); - - let Some(rhs) = self.parse_or(allow_constructors) else { - self.push_expected_expression(); - break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); - } - - Some(lhs) + None + } + }) } /// OrExpression /// = AndExpression ( '|' AndExpression )* pub(super) fn parse_or(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_and(allow_constructors)?; - - // Don't parse `x |= ...`, etc. - if self.next_is(Token::Assign) { - return Some(lhs); - } - - loop { - let operator = if self.eat(Token::Pipe) { - BinaryOpKind::Or + 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 { - break; - }; - let operator = Spanned::from(self.previous_token_span, operator); - - let Some(rhs) = self.parse_and(allow_constructors) else { - self.push_expected_expression(); - break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); - } - - Some(lhs) + None + } + }) } /// AndExpression /// = XorExpression ( '&' XorExpression )* pub(super) fn parse_and(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_xor(allow_constructors)?; - - // Don't parse `x &= ...`, etc. - if self.next_is(Token::Assign) { - return Some(lhs); - } - - loop { - let operator = if self.eat(Token::Ampersand) { - BinaryOpKind::And + 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 { - break; - }; - let operator = Spanned::from(self.previous_token_span, operator); - - let Some(rhs) = self.parse_xor(allow_constructors) else { - self.push_expected_expression(); - break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); - } - - Some(lhs) + None + } + }) } /// XorExpression /// = LessOrGreaterExpression ( '^' LessOrGreaterExpression )* pub(super) fn parse_xor(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_less_or_greater(allow_constructors)?; - - // Don't parse `x ^= ...`, etc. - if self.next_is(Token::Assign) { - return Some(lhs); - } - - loop { - let operator = if self.eat(Token::Caret) { - BinaryOpKind::Xor + 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 { - break; - }; - let operator = Spanned::from(self.previous_token_span, operator); - - let Some(rhs) = self.parse_less_or_greater(allow_constructors) else { - self.push_expected_expression(); - break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); - } - - Some(lhs) + None + } + }) } /// LessOrGreaterExpression /// = ShiftExpression ( ( '<' | '<=' | '>' | '>=' ) ShiftExpression )* pub(super) fn parse_less_or_greater(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_shift(allow_constructors)?; - - loop { - let operator = if self.eat(Token::Less) { - BinaryOpKind::Less - } else if self.eat(Token::LessEqual) { - BinaryOpKind::LessEqual - } else if self.next_token.token() != &Token::GreaterEqual && self.eat(Token::Greater) { + 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 `> >=`. - BinaryOpKind::Greater - } else if self.eat(Token::GreaterEqual) { - BinaryOpKind::GreaterEqual + Some(BinaryOpKind::Greater) + } else if parser.eat(Token::GreaterEqual) { + Some(BinaryOpKind::GreaterEqual) } else { - break; - }; - let operator = Spanned::from(self.previous_token_span, operator); - - let Some(rhs) = self.parse_shift(allow_constructors) else { - self.push_expected_expression(); - break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); - } - - Some(lhs) + None + } + }) } /// ShiftExpression /// = AddOrSubtractExpression ( ( '<<' | '>' '>' ) AddOrSubtractExpression )* pub(super) fn parse_shift(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_add_or_subtract(allow_constructors)?; - - loop { - let operator = if !self.next_is(Token::Assign) && self.eat(Token::ShiftLeft) { - BinaryOpKind::ShiftLeft - } else if self.tokens_follow(Token::Greater, Token::Greater) { + 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.tokens_follow(Token::Greater, 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. - self.next_token(); - self.next_token(); - BinaryOpKind::ShiftRight + parser.next_token(); + parser.next_token(); + Some(BinaryOpKind::ShiftRight) } else { - break; - }; - let operator = Spanned::from(self.previous_token_span, operator); - - let Some(rhs) = self.parse_add_or_subtract(allow_constructors) else { - self.push_expected_expression(); - break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); - } - - Some(lhs) + None + } + }) } /// AddOrSubtractExpression /// = MultiplyOrDivideOrModuloExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloExpression )* pub(super) fn parse_add_or_subtract(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_multiply_or_divide_or_modulo(allow_constructors)?; - - // Don't parse `x += ...`, etc. - if self.next_is(Token::Assign) { - return Some(lhs); - } - - loop { - let operator = if self.eat(Token::Plus) { - BinaryOpKind::Add - } else if self.eat(Token::Minus) { - BinaryOpKind::Subtract + 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 { - break; - }; - let operator = Spanned::from(self.previous_token_span, operator); - - let Some(rhs) = self.parse_multiply_or_divide_or_modulo(allow_constructors) else { - self.push_expected_expression(); - break; - }; - - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); - } - - Some(lhs) + None + } + }) } /// MultiplyOrDivideOrModuloExpression @@ -230,27 +132,42 @@ impl<'a> Parser<'a> { &mut self, allow_constructors: bool, ) -> Option { - let start_span = self.current_token_span; - let mut lhs = self.parse_term(allow_constructors)?; + 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 + } + }) + } - // Don't parse `x *= ...`, etc. - if self.next_is(Token::Assign) { - return Some(lhs); - } + 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 = if self.eat(Token::Star) { - BinaryOpKind::Multiply - } else if self.eat(Token::Slash) { - BinaryOpKind::Divide - } else if self.eat(Token::Percent) { - BinaryOpKind::Modulo - } else { + let operator_start_span = self.current_token_span; + let Some(operator) = op(self) else { break; }; - let operator = Spanned::from(self.previous_token_span, operator); + let operator = Spanned::from(operator_start_span, operator); - let Some(rhs) = self.parse_term(allow_constructors) else { + let Some(rhs) = next(self, allow_constructors) else { self.push_expected_expression(); break; }; From b57c371de6546b5a05e5b4df06dff365ee281458 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 4 Oct 2024 10:42:17 -0300 Subject: [PATCH 206/229] Introduce `parse_many` to reduce code duplication --- compiler/noirc_frontend/src/parser/labels.rs | 2 + compiler/noirc_frontend/src/parser/parser.rs | 9 +- .../src/parser/parser/arguments.rs | 28 +-- .../src/parser/parser/expression.rs | 176 +++++++++++------- .../src/parser/parser/function.rs | 119 ++++++------ .../src/parser/parser/generics.rs | 106 +++++------ .../src/parser/parser/lambda.rs | 28 +-- .../src/parser/parser/parse_many.rs | 102 ++++++++++ .../src/parser/parser/pattern.rs | 93 ++++----- .../src/parser/parser/structs.rs | 102 +++++----- .../noirc_frontend/src/parser/parser/types.rs | 74 +++----- .../src/parser/parser/use_tree.rs | 52 +++--- .../src/parser/parser/where_clause.rs | 123 +++++++----- 13 files changed, 546 insertions(+), 468 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/parse_many.rs diff --git a/compiler/noirc_frontend/src/parser/labels.rs b/compiler/noirc_frontend/src/parser/labels.rs index 82aaeadb4fd..b65837dbd5a 100644 --- a/compiler/noirc_frontend/src/parser/labels.rs +++ b/compiler/noirc_frontend/src/parser/labels.rs @@ -11,6 +11,7 @@ pub enum ParsingRuleLabel { Cast, Expression, FieldAccess, + GenericParameter, Global, Identifier, Integer, @@ -40,6 +41,7 @@ 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"), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 33e31790b61..d8a89611c20 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -24,6 +24,7 @@ mod item_visibility; mod lambda; mod modifiers; mod module; +mod parse_many; mod path; mod pattern; mod statement; @@ -393,18 +394,10 @@ impl<'a> Parser<'a> { self.eat(Token::Less) } - fn eat_greater(&mut self) -> bool { - self.eat(Token::Greater) - } - fn eat_assign(&mut self) -> bool { self.eat(Token::Assign) } - fn eat_plus(&mut self) -> bool { - self.eat(Token::Plus) - } - fn eat_dot(&mut self) -> bool { self.eat(Token::Dot) } diff --git a/compiler/noirc_frontend/src/parser/parser/arguments.rs b/compiler/noirc_frontend/src/parser/parser/arguments.rs index 738a4f31778..a4368ec32b1 100644 --- a/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -1,6 +1,6 @@ use crate::{ast::Expression, token::Token}; -use super::Parser; +use super::{parse_many::separated_by_comma_until_right_paren, Parser}; pub(crate) struct CallArguments { pub(crate) arguments: Vec, @@ -16,27 +16,11 @@ impl<'a> Parser<'a> { return None; } - if self.eat_right_paren() { - return Some(Vec::new()); - } - - let mut arguments = Vec::new(); - let mut trailing_comma = false; - loop { - let start_span = self.current_token_span; - let Some(expr) = self.parse_expression() else { - self.eat_right_paren(); - break; - }; - - if !trailing_comma && !arguments.is_empty() { - self.expected_token_separating_items(",", "arguments", start_span); - } - - arguments.push(expr); - - trailing_comma = self.eat_comma(); - } + let arguments = self.parse_many( + "arguments", + separated_by_comma_until_right_paren(), + Self::parse_expression_in_list, + ); Some(arguments) } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index d9edd3100ba..e05306c8570 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -7,11 +7,14 @@ use crate::{ Expression, ExpressionKind, GenericTypeArgs, Ident, IfExpression, IndexExpression, Literal, MemberAccessExpression, MethodCallExpression, Statement, TypePath, UnaryOp, UnresolvedType, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{labels::ParsingRuleLabel, parser::parse_many::separated_by_comma, ParserErrorReason}, token::{Keyword, Token, TokenKind}, }; -use super::Parser; +use super::{ + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, + Parser, +}; impl<'a> Parser<'a> { pub(crate) fn parse_expression_or_error(&mut self) -> Expression { @@ -398,33 +401,11 @@ impl<'a> Parser<'a> { /// /// ConstructorField = identifier ( ':' Expression )? fn parse_constructor(&mut self, typ: UnresolvedType) -> ExpressionKind { - let mut fields = Vec::new(); - let mut trailing_comma = false; - - loop { - let start_span = self.current_token_span; - let Some(ident) = self.eat_ident() else { - self.eat_or_error(Token::RightBrace); - break; - }; - - if !trailing_comma && !fields.is_empty() { - self.expected_token_separating_items(",", "constructor fields", start_span); - } - - if self.eat_colon() { - let expression = self.parse_expression_or_error(); - fields.push((ident, expression)); - } else { - fields.push((ident.clone(), ident.into())); - } - - trailing_comma = self.eat_commas(); - - if self.eat_right_brace() { - break; - } - } + let fields = self.parse_many( + "constructor fields", + separated_by_comma_until_right_brace(), + Self::parse_constructor_field, + ); ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, @@ -433,6 +414,19 @@ impl<'a> Parser<'a> { })) } + 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' Expression Block ( 'else' ( Block | IfExpression ) )? pub(super) fn parse_if_expr(&mut self) -> Option { if !self.eat_keyword(Keyword::If) { @@ -643,28 +637,21 @@ impl<'a> Parser<'a> { }); } - let mut exprs = vec![first_expr]; - let mut trailing_comma = self.eat_comma(); - loop { - if self.eat_right_bracket() { - break; - } + let comma_after_first_expr = self.eat_comma(); + let first_expr_span = self.current_token_span; - let start_span = self.current_token_span; - let Some(expr) = self.parse_expression() else { - self.eat_right_brace(); - break; - }; - - if !trailing_comma { - self.expected_token_separating_items(",", "expressions", start_span); - } - - exprs.push(expr); + let mut exprs = self.parse_many( + "expressions", + separated_by_comma().until(Token::RightBracket), + Self::parse_expression_in_list, + ); - trailing_comma = self.eat_commas(); + if !exprs.is_empty() && !comma_after_first_expr { + self.expected_token_separating_items(",", "expressions", first_expr_span); } + exprs.insert(0, first_expr); + Some(ArrayLiteral::Standard(exprs)) } @@ -697,27 +684,11 @@ impl<'a> Parser<'a> { return Some(ExpressionKind::Literal(Literal::Unit)); } - let mut exprs = Vec::new(); - let mut trailing_comma = false; - loop { - let start_span = self.current_token_span; - let Some(expr) = self.parse_expression() else { - self.expected_label(ParsingRuleLabel::Expression); - self.eat_right_paren(); - break; - }; - if !trailing_comma && !exprs.is_empty() { - self.expected_token_separating_items(",", "expressions", start_span); - } - - exprs.push(expr); - - trailing_comma = self.eat_commas(); - - if self.eat_right_paren() { - break; - } - } + 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))) @@ -726,6 +697,15 @@ impl<'a> Parser<'a> { }) } + 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() { @@ -1083,6 +1063,33 @@ mod tests { 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, ","); + 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]"; @@ -1251,6 +1258,39 @@ mod tests { 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, ","); + 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)"; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 7180ca67a90..5a524ad5cf5 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -15,6 +15,8 @@ use acvm::AcirField; use noirc_errors::Span; +use super::parse_many::separated_by_comma_until_right_paren; +use super::pattern::SelfPattern; use super::{pattern::PatternOrSelf, Parser}; pub(crate) struct FunctionDefinitionWithOptionalBody { @@ -130,21 +132,20 @@ impl<'a> Parser<'a> { /// /// FunctionParameter = Visibility PatternOrSelf ':' Type fn parse_function_parameters(&mut self, allow_self: bool) -> Vec { - let mut parameters = Vec::new(); - if !self.eat_left_paren() { - return parameters; + return Vec::new(); } - let mut trailing_comma = false; + 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 { - if self.eat_right_paren() { - break; - } - let start_span = self.current_token_span; - let pattern_or_self = if allow_self && parameters.is_empty() { + + let pattern_or_self = if allow_self { self.parse_pattern_or_self() } else { self.parse_pattern().map(PatternOrSelf::Pattern) @@ -152,77 +153,61 @@ impl<'a> Parser<'a> { let Some(pattern_or_self) = pattern_or_self else { self.expected_label(ParsingRuleLabel::Pattern); + // Let's try with the next token self.next_token(); if self.at_eof() { - break; + return None; } else { continue; } }; - if !trailing_comma && !parameters.is_empty() { - self.expected_token_separating_items(",", "parameters", start_span); - } + 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), + }); + } + } - match pattern_or_self { - PatternOrSelf::Pattern(pattern) => { - if self.eat_colon() { - let visibility = self.parse_visibility(); - - let typ = self.parse_type_or_error(); - parameters.push(Param { - visibility, - pattern, - typ, - span: self.span_since(start_span), - }); - } else { - self.push_error( - ParserErrorReason::MissingTypeForFunctionParameter, - Span::from(pattern.span().start()..self.current_token_span.end()), - ); - - parameters.push(Param { - visibility: Visibility::Private, - pattern, - typ: UnresolvedType { - typ: UnresolvedTypeData::Error, - span: Span::default(), - }, - span: self.span_since(start_span), - }); - } - } - PatternOrSelf::SelfPattern(self_pattern) => { - 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); - } + 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()), + ); - parameters.push(Param { - visibility: Visibility::Private, - pattern, - typ: self_type, - span: self.span_since(ident_span), - }); - } - } + 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) } + } - trailing_comma = self.eat_commas(); + 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); } - parameters + Param { + visibility: Visibility::Private, + pattern, + typ: self_type, + span: self.span_since(ident_span), + } } /// Visibility diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 2a6888ceb3a..89793834d3e 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -2,52 +2,38 @@ use noirc_errors::Span; use crate::{ ast::{ - GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UnresolvedTypeData, + GenericTypeArg, GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, }, parser::{labels::ParsingRuleLabel, ParserErrorReason}, token::{Keyword, Token, TokenKind}, }; -use super::Parser; +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 { - let mut generics = Vec::new(); - if !self.eat_less() { - return generics; - } - - if self.eat_greater() { - return generics; + return Vec::new(); } - let mut trailing_comma = false; - - loop { - let start_span = self.current_token_span; - let Some(generic) = self.parse_generic() else { - break; - }; - - if !trailing_comma && !generics.is_empty() { - self.expected_token_separating_items(",", "generic parameters", start_span); - } - - generics.push(generic); - - trailing_comma = self.eat_commas(); + self.parse_many( + "generic parameters", + separated_by_comma().until(Token::Greater), + Self::parse_generic_in_list, + ) + } - if self.eat_greater() { - break; - } + fn parse_generic_in_list(&mut self) -> Option { + if let Some(generic) = self.parse_generic() { + Some(generic) + } else { + self.expected_label(ParsingRuleLabel::GenericParameter); + None } - - generics } /// Generic @@ -140,43 +126,45 @@ impl<'a> Parser<'a> { return generic_type_args; } - let mut trailing_comma = false; - loop { - let start_span = self.current_token_span; - - if matches!(self.token.token(), Token::Ident(..)) && self.next_is(Token::Assign) { - let ident = self.eat_ident().unwrap(); + let generics = self.parse_many( + "generic parameters", + separated_by_comma().until(Token::Greater), + Self::parse_generic_type_arg, + ); - if !trailing_comma && !generic_type_args.is_empty() { - self.expected_token_separating_items(",", "generic parameters", start_span); + for generic in generics { + match generic { + GenericTypeArg::Ordered(typ) => { + generic_type_args.ordered_args.push(typ); } - - self.eat_assign(); - - let typ = self.parse_type_or_error(); - generic_type_args.named_args.push((ident, typ)); - } else { - let typ = self.parse_type_or_type_expression(); - let Some(typ) = typ else { - if generic_type_args.is_empty() { - self.expected_label(ParsingRuleLabel::TypeOrTypeExpression); - } - self.eat_greater(); - break; - }; - - if !trailing_comma && !generic_type_args.is_empty() { - self.expected_token_separating_items(",", "generic parameters", start_span); + GenericTypeArg::Named(name, typ) => { + generic_type_args.named_args.push((name, typ)); } - - generic_type_args.ordered_args.push(typ); } - - trailing_comma = self.eat_commas(); } 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 typ = self.parse_type_or_type_expression(); + let Some(typ) = typ else { + self.expected_label(ParsingRuleLabel::TypeOrTypeExpression); + return None; + }; + + Some(GenericTypeArg::Ordered(typ)) + } } fn type_u32() -> UnresolvedType { diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index 6c83dd633a1..811d394cf73 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -4,7 +4,7 @@ use crate::{ token::Token, }; -use super::Parser; +use super::{parse_many::separated_by_comma, Parser}; impl<'a> Parser<'a> { /// Lambda = '|' LambdaParameters? '|' ( '->' Type )? Expression @@ -30,37 +30,29 @@ impl<'a> Parser<'a> { } fn parse_lambda_parameters(&mut self) -> Vec<(Pattern, UnresolvedType)> { - let mut parameters = Vec::new(); - let mut trailing_comma = false; + self.parse_many( + "parameters", + separated_by_comma().until(Token::Pipe), + Self::parse_lambda_parameter, + ) + } + fn parse_lambda_parameter(&mut self) -> Option<(Pattern, UnresolvedType)> { loop { - if self.eat_pipe() { - break; - } - - let start_span = self.current_token_span; let Some(pattern) = self.parse_pattern() else { self.expected_label(ParsingRuleLabel::Pattern); // Let's try with the next token. self.next_token(); if self.at_eof() { - break; + return None; } else { continue; } }; - if !trailing_comma && !parameters.is_empty() { - self.expected_token_separating_items(",", "parameters", start_span); - } - let typ = self.parse_optional_type_annotation(); - parameters.push((pattern, typ)); - - trailing_comma = self.eat_commas(); + return Some((pattern, typ)); } - - parameters } } 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..90cd032a15b --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -0,0 +1,102 @@ +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 an item. 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 !trailing_separator && !elements.is_empty() { + self.expected_token_separating_items( + &separated_by.token.to_string(), + items, + start_span, + ); + } + + elements.push(element); + + trailing_separator = self.eat(separated_by.token.clone()); + + 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: Token, + 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, 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) +} diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index dbdf059c678..2b144d16bfe 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -6,7 +6,10 @@ use crate::{ token::{Keyword, Token, TokenKind}, }; -use super::Parser; +use super::{ + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, + Parser, +}; pub(crate) enum PatternOrSelf { Pattern(Pattern), @@ -114,6 +117,8 @@ impl<'a> Parser<'a> { /// /// 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); } @@ -133,7 +138,7 @@ impl<'a> Parser<'a> { }; if self.eat_left_brace() { - return Some(self.parse_struct_pattern(path)); + return Some(self.parse_struct_pattern(path, start_span)); } if !path.is_ident() { @@ -171,30 +176,22 @@ impl<'a> Parser<'a> { return None; } - let mut patterns = Vec::new(); - let mut trailing_comma = false; - loop { - if self.eat_right_paren() { - break; - } - - let start_span = self.current_token_span; - let Some(pattern) = self.parse_pattern() else { - self.expected_label(ParsingRuleLabel::Pattern); - self.eat_right_paren(); - break; - }; - - if !trailing_comma && !patterns.is_empty() { - self.expected_token_separating_items(",", "tuple elements", start_span); - } + let patterns = self.parse_many( + "tuple elements", + separated_by_comma_until_right_paren(), + Self::parse_tuple_pattern_element, + ); - patterns.push(pattern); + Some(Pattern::Tuple(patterns, self.span_since(start_span))) + } - trailing_comma = self.eat_commas(); + fn parse_tuple_pattern_element(&mut self) -> Option { + if let Some(pattern) = self.parse_pattern() { + Some(pattern) + } else { + self.expected_label(ParsingRuleLabel::Pattern); + None } - - Some(Pattern::Tuple(patterns, self.span_since(start_span))) } /// StructPattern = Path '{' StructPatternFields? '}' @@ -202,39 +199,27 @@ impl<'a> Parser<'a> { /// StructPatternFields = StructPatternField ( ',' StructPatternField )? ','? /// /// StructPatternField = identifier ( ':' Pattern )? - fn parse_struct_pattern(&mut self, path: Path) -> Pattern { - let start_span = path.span(); - - let mut patterns = Vec::new(); - let mut trailing_comma = false; - - loop { - if self.eat_right_brace() { - break; - } - - let start_span = self.current_token_span; - - let Some(ident) = self.eat_ident() else { - self.expected_identifier(); - self.eat_right_brace(); - break; - }; - - if !trailing_comma && !patterns.is_empty() { - self.expected_token_separating_items(",", "struct fields", start_span); - } - - if self.eat_colon() { - patterns.push((ident, self.parse_pattern_or_error())); - } else { - patterns.push((ident.clone(), Pattern::Identifier(ident))); - } + 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)) + } - trailing_comma = self.eat_commas(); - } + fn parse_struct_pattern_field(&mut self) -> Option<(Ident, Pattern)> { + let Some(ident) = self.eat_ident() else { + self.expected_identifier(); + return None; + }; - Pattern::Struct(path, patterns, self.span_since(start_span)) + Some(if self.eat_colon() { + (ident, self.parse_pattern_or_error()) + } else { + (ident.clone(), Pattern::Identifier(ident)) + }) } fn at_built_in_type(&self) -> bool { diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 9a1fb32dc54..ba66f1130d8 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -6,7 +6,7 @@ use crate::{ token::{Attribute, SecondaryAttribute, Token}, }; -use super::Parser; +use super::{parse_many::separated_by_comma_until_right_brace, Parser}; impl<'a> Parser<'a> { /// Struct = 'struct' identifier Generics '{' StructField* '}' @@ -42,59 +42,11 @@ impl<'a> Parser<'a> { return self.empty_struct(name, attributes, visibility, generics, start_span); } - let mut fields = Vec::new(); - let mut trailing_comma = false; - - 'outer: loop { - 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; - } - - 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); - break 'outer; - } - - // Or if we find a right brace - if self.eat_right_brace() { - break 'outer; - } - - self.expected_identifier(); - self.next_token(); - } - - let start_span = self.previous_token_span; - - self.eat_or_error(Token::Colon); - - let typ = self.parse_type_or_error(); - - if !trailing_comma && !fields.is_empty() { - self.expected_token_separating_items(",", "struct fields", start_span); - } - - fields.push(Documented::new(StructField { name, typ }, doc_comments)); - - trailing_comma = self.eat_commas(); - } + let fields = self.parse_many( + "struct fields", + separated_by_comma_until_right_brace(), + Self::parse_struct_field, + ); NoirStruct { name, @@ -106,6 +58,48 @@ impl<'a> Parser<'a> { } } + 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; + } + + 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.next_token(); + } + + 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, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index ccde29d2cc7..afe9bc2ef65 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -5,7 +5,7 @@ use crate::{ QuotedType, }; -use super::Parser; +use super::{parse_many::separated_by_comma_until_right_paren, Parser}; impl<'a> Parser<'a> { pub(crate) fn parse_type_or_error(&mut self) -> UnresolvedType { @@ -264,29 +264,11 @@ impl<'a> Parser<'a> { )); } - let mut args = Vec::new(); - let mut trailing_comma = false; - - loop { - if self.eat_right_paren() { - break; - } - - let start_span = self.current_token_span; - let typ = self.parse_type_or_error(); - if let UnresolvedTypeData::Unspecified = typ.typ { - self.eat_right_paren(); - break; - } - - if !trailing_comma && !args.is_empty() { - self.expected_token_separating_items(",", "parameters", start_span); - } - - args.push(typ); - - trailing_comma = self.eat_commas(); - } + 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() @@ -298,6 +280,15 @@ impl<'a> Parser<'a> { Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) } + fn parse_parameter(&mut self) -> Option { + let typ = self.parse_type_or_error(); + if let UnresolvedTypeData::Error = typ.typ { + None + } else { + Some(typ) + } + } + fn parse_trait_as_type(&mut self) -> Option { if !self.eat_keyword(Keyword::Impl) { return None; @@ -389,29 +380,18 @@ impl<'a> Parser<'a> { return Some(UnresolvedTypeData::Unit); } - let mut types = Vec::new(); - let mut trailing_comma = false; - loop { - let start_span = self.current_token_span; - - let Some(typ) = self.parse_type() else { - self.expected_label(ParsingRuleLabel::Type); - self.eat_right_paren(); - break; - }; - - if !trailing_comma && !types.is_empty() { - self.expected_token_separating_items(",", "tuple elements", start_span); - } - - types.push(typ); - - trailing_comma = self.eat_commas(); - - if self.eat_right_paren() { - break; - } - } + let (mut types, trailing_comma) = self.parse_many_return_trailing_separator_if_any( + "tuple elements", + separated_by_comma_until_right_paren(), + |parser| { + if let Some(typ) = parser.parse_type() { + Some(typ) + } else { + parser.expected_label(ParsingRuleLabel::Type); + None + } + }, + ); Some(if types.len() == 1 && !trailing_comma { UnresolvedTypeData::Parenthesized(Box::new(types.remove(0))) diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index e8d65f0f93d..e69eaf62601 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -6,7 +6,7 @@ use crate::{ token::{Keyword, Token}, }; -use super::Parser; +use super::{parse_many::separated_by_comma_until_right_brace, Parser}; impl<'a> Parser<'a> { /// Use = 'use' PathKind PathNoTurbofish UseTree @@ -50,34 +50,12 @@ impl<'a> Parser<'a> { if trailing_double_colon { if self.eat_left_brace() { - let mut use_trees = Vec::new(); - let mut trailing_comma = false; - loop { - let start_span = self.current_token_span; + let use_trees = self.parse_many( + "use trees", + separated_by_comma_until_right_brace(), + Self::parse_use_tree_in_list, + ); - let use_tree = self.parse_use_tree_without_kind( - self.current_token_span, - PathKind::Plain, - true, // nested - ); - - // If we didn't advance at all, we are done - if start_span == self.current_token_span { - break; - } - - if !trailing_comma && !use_trees.is_empty() { - self.expected_token_separating_items(",", "use trees", start_span); - } - - use_trees.push(use_tree); - - trailing_comma = self.eat_commas(); - - if self.eat_right_brace() { - break; - } - } UseTree { prefix, kind: UseTreeKind::List(use_trees) } } else { self.expected_token(Token::LeftBrace); @@ -88,6 +66,24 @@ impl<'a> Parser<'a> { } } + 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 { diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 4ef81afdc7f..7ea977f5310 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,10 +1,13 @@ use crate::{ - ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint}, + ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, parser::labels::ParsingRuleLabel, token::{Keyword, Token}, }; -use super::Parser; +use super::{ + parse_many::{separated_by, separated_by_comma}, + Parser, +}; impl<'a> Parser<'a> { /// WhereClause = 'where' WhereClauseItems? @@ -13,59 +16,53 @@ impl<'a> Parser<'a> { /// /// WhereClauseItem = Type ':' TraitBounds pub(super) fn parse_where_clause(&mut self) -> Vec { - let mut constraints = Vec::new(); - if !self.eat_keyword(Keyword::Where) { - return constraints; + return Vec::new(); } - let mut trailing_comma = false; - loop { - let start_span = self.current_token_span; - let Some(typ) = self.parse_type() else { - break; - }; - - if !trailing_comma && !constraints.is_empty() { - self.expected_token_separating_items(",", "trait bounds", start_span); - } + // 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); - self.eat_or_error(Token::Colon); + constraints + .into_iter() + .flat_map(|(typ, trait_bounds)| { + trait_bounds.into_iter().map(move |trait_bound| UnresolvedTraitConstraint { + typ: typ.clone(), + trait_bound, + }) + }) + .collect() + } - let trait_bounds = self.parse_trait_bounds(); - for trait_bound in trait_bounds { - constraints.push(UnresolvedTraitConstraint { typ: typ.clone(), trait_bound }); - } + fn parse_single_where_clause(&mut self) -> Option<(UnresolvedType, Vec)> { + let Some(typ) = self.parse_type() else { + return None; + }; - trailing_comma = self.eat_commas(); - } + self.eat_or_error(Token::Colon); - // Constraints might end up being empty, but that's accepted as valid syntax + let trait_bounds = self.parse_trait_bounds(); - constraints + Some((typ, trait_bounds)) } /// TraitBounds = TraitBound ( '+' TraitBound )? '+'? pub(super) fn parse_trait_bounds(&mut self) -> Vec { - let mut bounds = Vec::new(); - - let mut trailing_plus = false; - loop { - let start_span = self.current_token_span; - let Some(bound) = self.parse_trait_bound() else { - break; - }; - - if !trailing_plus && !bounds.is_empty() { - self.expected_token_separating_items("+", "trait bounds", start_span); - } - - bounds.push(bound); + self.parse_many( + "trait bounds", + separated_by(Token::Plus).stop_if_separator_is_missing(), + Self::parse_trait_bound_in_list, + ) + } - trailing_plus = self.eat_plus(); + 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 } - - bounds } pub(crate) fn parse_trait_bound_or_error(&mut self) -> TraitBound { @@ -95,7 +92,10 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::{parser::tests::expect_no_errors, Parser}; + use crate::parser::{ + parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, + Parser, ParserErrorReason, + }; #[test] fn parses_no_where_clause() { @@ -126,7 +126,7 @@ mod tests { #[test] fn parses_two_where_clauses() { - let src = "where Foo: Bar, i32: Qux"; + let src = "where Foo: Bar, i32: Qux {"; let mut parser = Parser::for_str(src); let mut constraints = parser.parse_where_clause(); expect_no_errors(&parser.errors); @@ -141,4 +141,41 @@ mod tests { 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, ","); + 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()); + } } From d98b3ca2ca863dc0e6d518664096012c4382ac34 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 4 Oct 2024 12:02:16 -0300 Subject: [PATCH 207/229] Let error take Token, and one more use of `parse_many` --- compiler/noirc_frontend/src/parser/errors.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 4 +- .../src/parser/parser/expression.rs | 11 +-- .../src/parser/parser/parse_many.rs | 6 +- .../src/parser/parser/type_expression.rs | 67 ++++++++++++++----- .../noirc_frontend/src/parser/parser/types.rs | 18 ++--- .../src/parser/parser/where_clause.rs | 13 ++-- 7 files changed, 78 insertions(+), 43 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 5b839b923d9..21f349419d4 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -18,7 +18,7 @@ pub enum ParserErrorReason { #[error("Unexpected `,`")] UnexpectedComma, #[error("Expected a `{token}` separating these two {items}")] - ExpectedTokenSeparatingTwoItems { token: String, items: String }, + ExpectedTokenSeparatingTwoItems { token: Token, items: String }, #[error("Invalid left-hand side of assignment")] InvalidLeftHandSideOfAssignment, #[error("Expected trait, found {found}")] diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index d8a89611c20..acb9e97d7c0 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -488,10 +488,10 @@ impl<'a> Parser<'a> { )); } - fn expected_token_separating_items(&mut self, token: &str, items: &str, span: Span) { + fn expected_token_separating_items(&mut self, token: Token, items: &str, span: Span) { self.push_error( ParserErrorReason::ExpectedTokenSeparatingTwoItems { - token: token.to_string(), + token: token, items: items.to_string(), }, span, diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index e05306c8570..a97b96f3903 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -638,7 +638,7 @@ impl<'a> Parser<'a> { } let comma_after_first_expr = self.eat_comma(); - let first_expr_span = self.current_token_span; + let second_expr_span = self.current_token_span; let mut exprs = self.parse_many( "expressions", @@ -647,7 +647,7 @@ impl<'a> Parser<'a> { ); if !exprs.is_empty() && !comma_after_first_expr { - self.expected_token_separating_items(",", "expressions", first_expr_span); + self.expected_token_separating_items(Token::Comma, "expressions", second_expr_span); } exprs.insert(0, first_expr); @@ -766,6 +766,7 @@ mod tests { }, Parser, ParserErrorReason, }, + token::Token, }; #[test] @@ -1014,7 +1015,7 @@ mod tests { let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); }; - assert_eq!(token, ","); + assert_eq!(token, &Token::Comma); assert_eq!(items, "expressions"); } @@ -1078,7 +1079,7 @@ mod tests { let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); }; - assert_eq!(token, ","); + assert_eq!(token, &Token::Comma); assert_eq!(items, "expressions"); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind @@ -1272,7 +1273,7 @@ mod tests { let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); }; - assert_eq!(token, ","); + assert_eq!(token, &Token::Comma); assert_eq!(items, "arguments"); let ExpressionKind::Call(call) = expr.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/parse_many.rs b/compiler/noirc_frontend/src/parser/parser/parse_many.rs index 90cd032a15b..b6824f745f1 100644 --- a/compiler/noirc_frontend/src/parser/parser/parse_many.rs +++ b/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -46,11 +46,7 @@ impl<'a> Parser<'a> { }; if !trailing_separator && !elements.is_empty() { - self.expected_token_separating_items( - &separated_by.token.to_string(), - items, - start_span, - ); + self.expected_token_separating_items(separated_by.token.clone(), items, start_span); } elements.push(element); diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 6c2cc670057..c3f21591efd 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -11,7 +11,7 @@ use crate::{ use acvm::acir::AcirField; use noirc_errors::Span; -use super::Parser; +use super::{parse_many::separated_by_comma_until_right_paren, Parser}; impl<'a> Parser<'a> { /// TypeExpression= AddOrSubtractTypeExpression @@ -333,12 +333,11 @@ impl<'a> Parser<'a> { // 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) - let mut typ_span = typ.span; 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, + span: typ.span, }); } @@ -349,24 +348,21 @@ impl<'a> Parser<'a> { }); } - let mut types = vec![typ]; - loop { - if !self.eat_commas() { - self.expected_token_separating_items(",", "tuple items", typ_span); - } + let comma_after_first_type = self.eat_comma(); + let second_type_span = self.current_token_span; - let Some(typ) = self.parse_type() else { - self.eat_or_error(Token::RightParen); - break; - }; - typ_span = typ.span; - types.push(typ); + let mut types = self.parse_many( + "tuple items", + separated_by_comma_until_right_paren(), + Self::parse_type_in_list, + ); - if self.eat_right_paren() { - break; - } + 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), @@ -417,7 +413,13 @@ mod tests { use crate::{ ast::{UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{parser::tests::expect_no_errors, Parser}, + parser::{ + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + token::Token, BinaryTypeOperator, }; @@ -591,6 +593,35 @@ mod tests { }; } + #[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,)"; diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index afe9bc2ef65..4bfc9f248d1 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -383,14 +383,7 @@ impl<'a> Parser<'a> { let (mut types, trailing_comma) = self.parse_many_return_trailing_separator_if_any( "tuple elements", separated_by_comma_until_right_paren(), - |parser| { - if let Some(typ) = parser.parse_type() { - Some(typ) - } else { - parser.expected_label(ParsingRuleLabel::Type); - None - } - }, + Self::parse_type_in_list, ); Some(if types.len() == 1 && !trailing_comma { @@ -400,6 +393,15 @@ impl<'a> Parser<'a> { }) } + 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() { diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 7ea977f5310..e3059dfb28f 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -92,9 +92,14 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::{ - parser::tests::{expect_no_errors, get_single_error_reason, get_source_with_error_span}, - Parser, ParserErrorReason, + use crate::{ + parser::{ + parser::tests::{ + expect_no_errors, get_single_error_reason, get_source_with_error_span, + }, + Parser, ParserErrorReason, + }, + token::Token, }; #[test] @@ -156,7 +161,7 @@ mod tests { let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); }; - assert_eq!(token, ","); + assert_eq!(token, &Token::Comma); assert_eq!(items, "where clauses"); assert_eq!(constraints.len(), 2); From 8631a0c90c30674fefe6d92ba9e25b9420b53e49 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 4 Oct 2024 12:14:25 -0300 Subject: [PATCH 208/229] parse_many without separator --- compiler/noirc_frontend/src/parser/parser.rs | 4 - .../src/parser/parser/expression.rs | 34 ++++--- .../noirc_frontend/src/parser/parser/impls.rs | 98 +++++++++++-------- .../noirc_frontend/src/parser/parser/item.rs | 39 +++----- .../src/parser/parser/parse_many.rs | 20 +++- .../src/parser/parser/traits.rs | 41 ++++---- 6 files changed, 126 insertions(+), 110 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index acb9e97d7c0..3cdb9e70af4 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -378,10 +378,6 @@ impl<'a> Parser<'a> { self.eat(Token::LeftBrace) } - fn eat_right_brace(&mut self) -> bool { - self.eat(Token::RightBrace) - } - fn eat_left_bracket(&mut self) -> bool { self.eat(Token::LeftBracket) } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a97b96f3903..59ec6cb714d 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -12,7 +12,10 @@ use crate::{ }; use super::{ - parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, + parse_many::{ + separated_by_comma_until_right_brace, separated_by_comma_until_right_paren, + without_separator, + }, Parser, }; @@ -712,27 +715,26 @@ impl<'a> Parser<'a> { return None; } - let mut statements: Vec<(Statement, (Option, Span))> = Vec::new(); - - loop { - if self.eat_right_brace() { - break; - } - - let Some((statement, (token, span))) = self.parse_statement() else { - self.expected_label(ParsingRuleLabel::Statement); - self.eat_right_brace(); - break; - }; - - statements.push((statement, (token, span))); - } + 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))>, diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 05d7ff4194d..185773c2016 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -10,7 +10,7 @@ use crate::{ token::{Keyword, Token}, }; -use super::Parser; +use super::{parse_many::without_separator, Parser}; pub(crate) enum Impl { Impl(TypeImpl), @@ -66,21 +66,23 @@ impl<'a> Parser<'a> { /// /// TypeImplItem = OuterDocComments Attributes Modifiers Function fn parse_type_impl_body(&mut self) -> Vec<(Documented, Span)> { - let mut methods = Vec::new(); - if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return methods; + return Vec::new(); } - loop { - if self.eat_right_brace() { - break; - } + 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)> { + loop { if self.at_eof() { self.expected_token(Token::RightBrace); - break; + return None; } let doc_comments = self.parse_outer_doc_comments(); @@ -90,27 +92,29 @@ impl<'a> Parser<'a> { false, // allow mutable ); - if self.eat_keyword(Keyword::Fn) { - let method = self.parse_function( - attributes, - modifiers.visibility, - modifiers.comptime.is_some(), - modifiers.unconstrained.is_some(), - true, // allow_self - ); - methods.push((Documented::new(method, doc_comments), self.span_since(start_span))); - continue; - } + if !self.eat_keyword(Keyword::Fn) { + self.modifiers_not_followed_by_an_item(modifiers); - self.modifiers_not_followed_by_an_item(modifiers); + if !self.at(Token::RightBrace) { + self.expected_token(Token::Keyword(Keyword::Fn)); + + // Try with the next token + self.next_token(); + continue; + } - if self.token.token() != &Token::RightBrace { - self.expected_token(Token::Keyword(Keyword::Fn)); - self.next_token(); + return None; } - } - methods + let method = self.parse_function( + attributes, + modifiers.visibility, + modifiers.comptime.is_some(), + modifiers.unconstrained.is_some(), + true, // allow_self + ); + return Some((Documented::new(method, doc_comments), self.span_since(start_span))); + } } /// TraitImpl = 'impl' Generics Path GenericTypeArgs 'for' Type TraitImplBody @@ -136,35 +140,43 @@ impl<'a> Parser<'a> { /// TraitImplBody = '{' TraitImplItem* '}' fn parse_trait_impl_body(&mut self) -> Vec> { - let mut items = Vec::new(); - if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return items; + 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> { loop { + if self.at_eof() { + self.expected_token(Token::RightBrace); + return None; + } + let start_span = self.current_token_span; let doc_comments = self.parse_outer_doc_comments(); - if let Some(kind) = self.parse_trait_impl_item_kind() { - let item = TraitImplItem { kind, span: self.span_since(start_span) }; - items.push(Documented::new(item, doc_comments)); + let Some(kind) = self.parse_trait_impl_item_kind() else { + if !self.at(Token::RightBrace) { + self.expected_label(ParsingRuleLabel::TraitImplItem); - if self.eat_right_brace() { - break; + // Try with the next token + self.next_token(); + continue; } - } else if self.at_eof() || self.eat_right_brace() { - break; - } else { - self.expected_label(ParsingRuleLabel::TraitImplItem); - // Keep going - self.next_token(); - } - } + return None; + }; - items + let item = TraitImplItem { kind, span: self.span_since(start_span) }; + return Some(Documented::new(item, doc_comments)); + } } /// TraitImplItem diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index ea9afb1afa1..0cefd5b1ffa 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -3,7 +3,7 @@ use crate::{ token::{Keyword, Token}, }; -use super::{impls::Impl, Parser}; +use super::{impls::Impl, parse_many::without_separator, Parser}; impl<'a> Parser<'a> { pub(crate) fn parse_top_level_items(&mut self) -> Vec { @@ -13,48 +13,39 @@ impl<'a> Parser<'a> { } pub(crate) fn parse_items(&mut self, nested: bool) -> Vec { - let mut items = Vec::new(); + self.parse_many("items", without_separator(), |parser| parser.parse_item_in_list(nested)) + } + fn parse_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) { - break; + 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() { - break; + return None; } if let Some(item) = self.parse_item() { - items.push(item); - continue; + return Some(item); } // If we couldn't parse an item we check which token we got match self.token.token() { - Token::RightBrace => { - if nested { - break; - } else { - self.expected_label(ParsingRuleLabel::Item); - // We'll try parsing an item starting on the next token - self.next_token(); - } - } - Token::EOF => { - break; - } - _ => { - self.expected_label(ParsingRuleLabel::Item); - // We'll try parsing an item starting on the next token - self.next_token(); + Token::RightBrace if nested => { + return None; } + Token::EOF => return None, + _ => (), } - } - items + self.expected_label(ParsingRuleLabel::Item); + // We'll try parsing an item starting on the next token + self.next_token(); + } } /// Item = OuterDocComments ItemKind diff --git a/compiler/noirc_frontend/src/parser/parser/parse_many.rs b/compiler/noirc_frontend/src/parser/parser/parse_many.rs index b6824f745f1..04d3f34766c 100644 --- a/compiler/noirc_frontend/src/parser/parser/parse_many.rs +++ b/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -45,13 +45,19 @@ impl<'a> Parser<'a> { break; }; - if !trailing_separator && !elements.is_empty() { - self.expected_token_separating_items(separated_by.token.clone(), items, start_span); + 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 = self.eat(separated_by.token.clone()); + 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 { @@ -66,7 +72,7 @@ impl<'a> Parser<'a> { } pub(super) struct SeparatedBy { - pub(super) token: Token, + pub(super) token: Option, pub(super) until: Option, pub(super) continue_if_separator_is_missing: bool, } @@ -82,7 +88,7 @@ impl SeparatedBy { } pub(super) fn separated_by(token: Token) -> SeparatedBy { - SeparatedBy { token, until: None, continue_if_separator_is_missing: true } + SeparatedBy { token: Some(token), until: None, continue_if_separator_is_missing: true } } pub(super) fn separated_by_comma() -> SeparatedBy { @@ -96,3 +102,7 @@ pub(super) fn separated_by_comma_until_right_paren() -> SeparatedBy { 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/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 8c0cc4e1d1e..397b9b623d6 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -7,6 +7,7 @@ use crate::{ token::{Attribute, Keyword, SecondaryAttribute, Token}, }; +use super::parse_many::without_separator; use super::Parser; impl<'a> Parser<'a> { @@ -41,37 +42,41 @@ impl<'a> Parser<'a> { /// TraitBody = '{' ( OuterDocComments TraitItem )* '}' fn parse_trait_body(&mut self) -> Vec> { - let mut items = Vec::new(); - if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return items; + return Vec::new(); } - loop { - if self.eat_right_brace() { - break; - } + self.parse_many( + "trait items", + without_separator().until(Token::RightBrace), + Self::parse_trait_item_in_list, + ) + } + fn parse_trait_item_in_list(&mut self) -> Option> { + loop { if self.at_eof() { self.expected_token(Token::RightBrace); - break; + return None; } let doc_comments = self.parse_outer_doc_comments(); - if let Some(item) = self.parse_trait_item() { - items.push(Documented::new(item, doc_comments)); - continue; - } + let Some(item) = self.parse_trait_item() else { + if !self.at(Token::RightBrace) { + self.expected_label(ParsingRuleLabel::TraitItem); - if self.token.token() != &Token::RightBrace { - self.expected_label(ParsingRuleLabel::TraitItem); - self.next_token(); - } - } + // Try with the next token + self.next_token(); + continue; + } - items + return None; + }; + + return Some(Documented::new(item, doc_comments)); + } } /// TraitItem From 601f3a70d3a553b938808145a2fb41e007156d1e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 4 Oct 2024 13:05:20 -0300 Subject: [PATCH 209/229] parse_item_in_list --- compiler/noirc_frontend/src/parser/errors.rs | 2 +- compiler/noirc_frontend/src/parser/labels.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 12 +-- .../src/parser/parser/expression.rs | 6 +- .../noirc_frontend/src/parser/parser/impls.rs | 92 +++++++------------ .../noirc_frontend/src/parser/parser/item.rs | 68 ++++++++++---- .../src/parser/parser/traits.rs | 26 +----- .../src/parser/parser/type_expression.rs | 2 +- .../src/parser/parser/where_clause.rs | 2 +- 9 files changed, 100 insertions(+), 112 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 21f349419d4..f9cc539d7b7 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -18,7 +18,7 @@ pub enum ParserErrorReason { #[error("Unexpected `,`")] UnexpectedComma, #[error("Expected a `{token}` separating these two {items}")] - ExpectedTokenSeparatingTwoItems { token: Token, items: String }, + ExpectedTokenSeparatingTwoItems { token: Token, items: &'static str }, #[error("Invalid left-hand side of assignment")] InvalidLeftHandSideOfAssignment, #[error("Expected trait, found {found}")] diff --git a/compiler/noirc_frontend/src/parser/labels.rs b/compiler/noirc_frontend/src/parser/labels.rs index b65837dbd5a..5c9ec236e07 100644 --- a/compiler/noirc_frontend/src/parser/labels.rs +++ b/compiler/noirc_frontend/src/parser/labels.rs @@ -59,7 +59,7 @@ impl fmt::Display for ParsingRuleLabel { ParsingRuleLabel::Type => write!(f, "type"), ParsingRuleLabel::TypeExpression => write!(f, "type expression"), ParsingRuleLabel::TypeOrTypeExpression => write!(f, "type or type expression"), - ParsingRuleLabel::TokenKind(token_kind) => write!(f, "{token_kind:?}"), + 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/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 3cdb9e70af4..e5d0f6b3d22 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -135,7 +135,7 @@ impl<'a> Parser<'a> { /// 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_items(nested); + let items = self.parse_module_items(nested); ParsedModule { items, inner_doc_comments } } @@ -484,14 +484,8 @@ impl<'a> Parser<'a> { )); } - fn expected_token_separating_items(&mut self, token: Token, items: &str, span: Span) { - self.push_error( - ParserErrorReason::ExpectedTokenSeparatingTwoItems { - token: token, - items: items.to_string(), - }, - span, - ); + fn expected_token_separating_items(&mut self, token: Token, items: &'static str, span: Span) { + self.push_error(ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, span); } fn modifiers_not_followed_by_an_item(&mut self, modifiers: Modifiers) { diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 59ec6cb714d..772e68b9079 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1018,7 +1018,7 @@ mod tests { panic!("Expected a different error"); }; assert_eq!(token, &Token::Comma); - assert_eq!(items, "expressions"); + assert_eq!(*items, "expressions"); } #[test] @@ -1082,7 +1082,7 @@ mod tests { panic!("Expected a different error"); }; assert_eq!(token, &Token::Comma); - assert_eq!(items, "expressions"); + assert_eq!(*items, "expressions"); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { @@ -1276,7 +1276,7 @@ mod tests { panic!("Expected a different error"); }; assert_eq!(token, &Token::Comma); - assert_eq!(items, "arguments"); + assert_eq!(*items, "arguments"); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 185773c2016..8829193d471 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -7,7 +7,7 @@ use crate::{ UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, parser::{labels::ParsingRuleLabel, ParserErrorReason}, - token::{Keyword, Token}, + token::{Keyword, Token, TokenKind}, }; use super::{parse_many::without_separator, Parser}; @@ -79,42 +79,31 @@ impl<'a> Parser<'a> { } fn parse_type_impl_method(&mut self) -> Option<(Documented, Span)> { - loop { - if self.at_eof() { - self.expected_token(Token::RightBrace); - return None; - } - - let doc_comments = self.parse_outer_doc_comments(); - let start_span = self.current_token_span; - let attributes = self.parse_attributes(); - let modifiers = self.parse_modifiers( - false, // allow mutable - ); - - if !self.eat_keyword(Keyword::Fn) { - self.modifiers_not_followed_by_an_item(modifiers); - - if !self.at(Token::RightBrace) { - self.expected_token(Token::Keyword(Keyword::Fn)); + 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 + ); - // Try with the next token - self.next_token(); - continue; + 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 } - - return None; - } - - let method = self.parse_function( - attributes, - modifiers.visibility, - modifiers.comptime.is_some(), - modifiers.unconstrained.is_some(), - true, // allow_self - ); - return Some((Documented::new(method, doc_comments), self.span_since(start_span))); - } + }, + ) } /// TraitImpl = 'impl' Generics Path GenericTypeArgs 'for' Type TraitImplBody @@ -153,30 +142,17 @@ impl<'a> Parser<'a> { } fn parse_trait_impl_item(&mut self) -> Option> { - loop { - if self.at_eof() { - self.expected_token(Token::RightBrace); - return None; - } - - let start_span = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); - - let Some(kind) = self.parse_trait_impl_item_kind() else { - if !self.at(Token::RightBrace) { - self.expected_label(ParsingRuleLabel::TraitImplItem); - - // Try with the next token - self.next_token(); - continue; - } + self.parse_item_in_list(ParsingRuleLabel::TraitImplItem, |parser| { + let start_span = parser.current_token_span; + let doc_comments = parser.parse_outer_doc_comments(); - return None; - }; - - let item = TraitImplItem { kind, span: self.span_since(start_span) }; - return Some(Documented::new(item, 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 diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 0cefd5b1ffa..97772c9f437 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -7,16 +7,18 @@ 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_items( + self.parse_module_items( false, // nested ) } - pub(crate) fn parse_items(&mut self, nested: bool) -> Vec { - self.parse_many("items", without_separator(), |parser| parser.parse_item_in_list(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_item_in_list(&mut self, nested: bool) -> Option { + 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) { @@ -29,22 +31,56 @@ impl<'a> Parser<'a> { return None; } - if let Some(item) = self.parse_item() { - return Some(item); + 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.next_token(); + 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; } - // If we couldn't parse an item we check which token we got - match self.token.token() { - Token::RightBrace if nested => { - return None; + let Some(item) = f(self) else { + if !self.at(Token::RightBrace) { + self.expected_label(label.clone()); + + // Try with the next token + self.next_token(); + continue; } - Token::EOF => return None, - _ => (), - } - self.expected_label(ParsingRuleLabel::Item); - // We'll try parsing an item starting on the next token - self.next_token(); + return None; + }; + + return Some(item); } } diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 397b9b623d6..f3b6e956ccb 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -55,28 +55,10 @@ impl<'a> Parser<'a> { } fn parse_trait_item_in_list(&mut self) -> Option> { - loop { - if self.at_eof() { - self.expected_token(Token::RightBrace); - return None; - } - - let doc_comments = self.parse_outer_doc_comments(); - - let Some(item) = self.parse_trait_item() else { - if !self.at(Token::RightBrace) { - self.expected_label(ParsingRuleLabel::TraitItem); - - // Try with the next token - self.next_token(); - continue; - } - - return None; - }; - - return Some(Documented::new(item, doc_comments)); - } + 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)) + }) } /// TraitItem diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index c3f21591efd..526c5994a87 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -609,7 +609,7 @@ mod tests { panic!("Expected a different error"); }; assert_eq!(token, &Token::Comma); - assert_eq!(items, "tuple items"); + assert_eq!(*items, "tuple items"); let UnresolvedTypeData::Tuple(types) = typ.typ else { panic!("Expected tuple type"); diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index e3059dfb28f..333ab71d6f7 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -162,7 +162,7 @@ mod tests { panic!("Expected a different error"); }; assert_eq!(token, &Token::Comma); - assert_eq!(items, "where clauses"); + assert_eq!(*items, "where clauses"); assert_eq!(constraints.len(), 2); From 888abbd4e3df79fa4cccaa8b646285ae39a5fe1e Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 4 Oct 2024 13:13:18 -0300 Subject: [PATCH 210/229] Rename variable for clarity --- compiler/noirc_frontend/src/parser/parser/expression.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 772e68b9079..e7cd52f5e24 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -453,9 +453,9 @@ impl<'a> Parser<'a> { let alternative = if self.eat_keyword(Keyword::Else) { let start_span = self.current_token_span; - if let Some(alternative) = self.parse_block() { + if let Some(block) = self.parse_block() { let span = self.span_since(start_span); - Some(Expression { kind: ExpressionKind::Block(alternative), 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 { From 712254984c29b81b75a75a690d896e1582b586ed Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 5 Oct 2024 09:48:29 -0300 Subject: [PATCH 211/229] Check double colon after path kind in one place --- compiler/noirc_frontend/src/parser/parser/path.rs | 9 +++++---- compiler/noirc_frontend/src/parser/parser/use_tree.rs | 3 --- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index ea900e46e1b..fe8ab24a441 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -68,9 +68,6 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let kind = self.parse_path_kind(); - if kind != PathKind::Plain { - self.eat_or_error(Token::DoubleColon); - } let path = self.parse_optional_path_after_kind( kind, @@ -179,7 +176,7 @@ impl<'a> Parser<'a> { /// | 'super' '::' /// | nothing pub(super) fn parse_path_kind(&mut self) -> PathKind { - if self.eat_keyword(Keyword::Crate) { + let kind = if self.eat_keyword(Keyword::Crate) { PathKind::Crate } else if self.eat_keyword(Keyword::Dep) { PathKind::Dep @@ -187,7 +184,11 @@ impl<'a> Parser<'a> { PathKind::Super } else { PathKind::Plain + }; + if kind != PathKind::Plain { + self.eat_or_error(Token::DoubleColon); } + kind } /// AsTraitPath = '<' Type 'as' PathNoTurbofish GenericTypeArgs '>' '::' identifier diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index e69eaf62601..0e90a105c97 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -18,9 +18,6 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let kind = self.parse_path_kind(); - if kind != PathKind::Plain { - self.eat_or_error(Token::DoubleColon); - } let use_tree = self.parse_use_tree_without_kind( start_span, kind, false, // nested From 99781313ab94e44927e906fa604c9aad57e33147 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 5 Oct 2024 13:46:33 -0300 Subject: [PATCH 212/229] A bit more grammar --- .../noirc_frontend/src/parser/parser/expression.rs | 9 ++++++--- .../noirc_frontend/src/parser/parser/statement.rs | 12 +++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index e7cd52f5e24..f2166e7f6d6 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -31,7 +31,10 @@ impl<'a> Parser<'a> { /// 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. - pub(crate) fn parse_expression_no_constructors_or_error(&mut self) -> Expression { + /// 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 } @@ -430,13 +433,13 @@ impl<'a> Parser<'a> { }) } - /// IfExpression = 'if' Expression Block ( 'else' ( Block | IfExpression ) )? + /// 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_no_constructors_or_error(); + let condition = self.parse_expression_except_constructor_or_error(); let start_span = self.current_token_span; let Some(consequence) = self.parse_block() else { diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 6684ac42386..4fd06b95735 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -75,6 +75,8 @@ impl<'a> Parser<'a> { /// /// ReturnStatement = 'return' Expression? /// + /// IfStatement = IfExpression + /// /// BlockStatement = Block /// /// AssignStatement = Expression '=' Expression @@ -258,16 +260,16 @@ impl<'a> Parser<'a> { } /// ForRange - /// = Expression - /// | Expression '..' Expression + /// = ExpressionExceptConstructor + /// | ExpressionExceptConstructor '..' ExpressionExceptConstructor fn parse_for_range(&mut self) -> ForRange { - let expr = self.parse_expression_no_constructors_or_error(); + let expr = self.parse_expression_except_constructor_or_error(); if self.eat(Token::DoubleDot) { - let end = self.parse_expression_no_constructors_or_error(); + 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_no_constructors_or_error(); + let end = self.parse_expression_except_constructor_or_error(); ForRange::Range(ForBounds { start: expr, end, inclusive: true }) } else { ForRange::Array(expr) From 728f3adec5370994c27210da9cf5e128d91f7e83 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 08:15:50 -0300 Subject: [PATCH 213/229] Make sure file is formatted by test --- tooling/nargo_fmt/tests/input/databus.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/nargo_fmt/tests/input/databus.nr b/tooling/nargo_fmt/tests/input/databus.nr index 0e9761ed52d..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(0) u8) -> return_data u32 {} +fn main(x: pub u8, y: call_data(0) u8) -> return_data u32 { } From 7337d885f6e57a26eb0cb3bdfadffd88dbc7a778 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:09:05 -0300 Subject: [PATCH 214/229] Better test for checking operator precedence --- .../src/parser/parser/expression.rs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index f2166e7f6d6..baa97f91a59 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1554,7 +1554,37 @@ mod tests { #[test] fn parses_operator_precedence() { - let src = "1 + 2 * 3 + 4"; + // 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 mut parser = Parser::for_str(src); let expr = parser.parse_expression_or_error(); assert_eq!(expr.span.end() as usize, src.len()); @@ -1562,9 +1592,7 @@ mod tests { let ExpressionKind::Infix(infix_expr) = expr.kind else { panic!("Expected infix"); }; - assert_eq!(infix_expr.lhs.to_string(), "(1 + (2 * 3))"); - assert_eq!(infix_expr.operator.contents, BinaryOpKind::Add); - assert_eq!(infix_expr.rhs.to_string(), "4"); + assert_eq!(infix_expr.to_string(), expected_src); } #[test] From ebd9ff2a19dc3e86d8e1dfcd44bc4fb1039b7832 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:32:11 -0300 Subject: [PATCH 215/229] Remove Default from Token and Spanned --- compiler/noirc_frontend/src/lexer/token.rs | 5 +-- compiler/noirc_frontend/src/parser/parser.rs | 46 +++++++++----------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 33c9d08128e..d79a184d4c4 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -126,7 +126,7 @@ pub enum BorrowedToken<'input> { Invalid(char), } -#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord, Default)] +#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] pub enum Token { Ident(String), Int(FieldElement), @@ -227,7 +227,6 @@ pub enum Token { /// $ DollarSign, #[allow(clippy::upper_case_acronyms)] - #[default] EOF, Whitespace(String), @@ -314,7 +313,7 @@ pub enum DocStyle { Inner, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SpannedToken(Spanned); impl PartialEq for Token { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index e5d0f6b3d22..4accf7dcec2 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -116,8 +116,8 @@ impl<'a> Parser<'a> { let mut parser = Self { errors: Vec::new(), tokens, - token: SpannedToken::default(), - next_token: SpannedToken::default(), + token: eof_spanned_token(), + next_token: eof_spanned_token(), current_token_span: Default::default(), previous_token_span: Default::default(), }; @@ -162,13 +162,14 @@ impl<'a> Parser<'a> { } } - fn next_token(&mut self) { + /// Bumps this parser by one token. Returns the token that was previously the "current" token. + fn next_token(&mut self) -> SpannedToken { self.previous_token_span = self.current_token_span; - let token = self.read_token_internal(); - let next_token = std::mem::take(&mut self.next_token); - self.token = next_token; - self.next_token = token; + 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 } fn read_two_first_tokens(&mut self) { @@ -186,16 +187,14 @@ impl<'a> Parser<'a> { Err(lexer_error) => self.errors.push(lexer_error.into()), } } else { - return SpannedToken::default(); + return eof_spanned_token(); } } } fn eat_kind(&mut self, kind: TokenKind) -> Option { if self.token.kind() == kind { - let token = std::mem::take(&mut self.token); - self.next_token(); - Some(token) + Some(self.next_token()) } else { None } @@ -239,8 +238,7 @@ impl<'a> Parser<'a> { fn eat_int_type(&mut self) -> Option { let is_int_type = matches!(self.token.token(), Token::IntType(..)); if is_int_type { - let token = std::mem::take(&mut self.token); - self.next_token(); + let token = self.next_token(); match token.into_token() { Token::IntType(int_type) => Some(int_type), _ => unreachable!(), @@ -252,8 +250,7 @@ impl<'a> Parser<'a> { fn eat_int(&mut self) -> Option { if matches!(self.token.token(), Token::Int(..)) { - let token = std::mem::take(&mut self.token); - self.next_token(); + let token = self.next_token(); match token.into_token() { Token::Int(int) => Some(int), _ => unreachable!(), @@ -265,8 +262,7 @@ impl<'a> Parser<'a> { fn eat_bool(&mut self) -> Option { if matches!(self.token.token(), Token::Bool(..)) { - let token = std::mem::take(&mut self.token); - self.next_token(); + let token = self.next_token(); match token.into_token() { Token::Bool(bool) => Some(bool), _ => unreachable!(), @@ -278,8 +274,7 @@ impl<'a> Parser<'a> { fn eat_str(&mut self) -> Option { if matches!(self.token.token(), Token::Str(..)) { - let token = std::mem::take(&mut self.token); - self.next_token(); + let token = self.next_token(); match token.into_token() { Token::Str(string) => Some(string), _ => unreachable!(), @@ -291,8 +286,7 @@ impl<'a> Parser<'a> { fn eat_raw_str(&mut self) -> Option<(String, u8)> { if matches!(self.token.token(), Token::RawStr(..)) { - let token = std::mem::take(&mut self.token); - self.next_token(); + let token = self.next_token(); match token.into_token() { Token::RawStr(string, n) => Some((string, n)), _ => unreachable!(), @@ -304,8 +298,7 @@ impl<'a> Parser<'a> { fn eat_fmt_str(&mut self) -> Option { if matches!(self.token.token(), Token::FmtStr(..)) { - let token = std::mem::take(&mut self.token); - self.next_token(); + let token = self.next_token(); match token.into_token() { Token::FmtStr(string) => Some(string), _ => unreachable!(), @@ -317,8 +310,7 @@ impl<'a> Parser<'a> { fn eat_quote(&mut self) -> Option { if matches!(self.token.token(), Token::Quote(..)) { - let token = std::mem::take(&mut self.token); - self.next_token(); + let token = self.next_token(); match token.into_token() { Token::Quote(tokens) => Some(tokens), _ => unreachable!(), @@ -545,3 +537,7 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::with_reason(reason, span)); } } + +fn eof_spanned_token() -> SpannedToken { + SpannedToken::new(Token::EOF, Default::default()) +} From 3f712d0088c14aa16cb6cca385f4c75ea96fe88a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:32:32 -0300 Subject: [PATCH 216/229] next_token -> bump --- compiler/noirc_frontend/src/parser/parser.rs | 24 +++++++++---------- .../src/parser/parser/arguments.rs | 2 +- .../src/parser/parser/expression.rs | 6 ++--- .../src/parser/parser/function.rs | 2 +- .../noirc_frontend/src/parser/parser/infix.rs | 4 ++-- .../noirc_frontend/src/parser/parser/item.rs | 4 ++-- .../src/parser/parser/lambda.rs | 2 +- .../noirc_frontend/src/parser/parser/path.rs | 4 ++-- .../src/parser/parser/pattern.rs | 4 ++-- .../src/parser/parser/statement.rs | 8 +++---- .../src/parser/parser/structs.rs | 2 +- .../src/parser/parser/type_expression.rs | 2 +- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 4accf7dcec2..e136085fb36 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -163,7 +163,7 @@ impl<'a> Parser<'a> { } /// Bumps this parser by one token. Returns the token that was previously the "current" token. - fn next_token(&mut self) -> SpannedToken { + 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); @@ -194,7 +194,7 @@ impl<'a> Parser<'a> { fn eat_kind(&mut self, kind: TokenKind) -> Option { if self.token.kind() == kind { - Some(self.next_token()) + Some(self.bump()) } else { None } @@ -203,7 +203,7 @@ impl<'a> Parser<'a> { fn eat_keyword(&mut self, keyword: Keyword) -> bool { if let Token::Keyword(kw) = self.token.token() { if *kw == keyword { - self.next_token(); + self.bump(); true } else { false @@ -227,7 +227,7 @@ impl<'a> Parser<'a> { fn eat_self(&mut self) -> bool { if let Token::Ident(ident) = self.token.token() { if ident == "self" { - self.next_token(); + self.bump(); return true; } } @@ -238,7 +238,7 @@ impl<'a> Parser<'a> { fn eat_int_type(&mut self) -> Option { let is_int_type = matches!(self.token.token(), Token::IntType(..)); if is_int_type { - let token = self.next_token(); + let token = self.bump(); match token.into_token() { Token::IntType(int_type) => Some(int_type), _ => unreachable!(), @@ -250,7 +250,7 @@ impl<'a> Parser<'a> { fn eat_int(&mut self) -> Option { if matches!(self.token.token(), Token::Int(..)) { - let token = self.next_token(); + let token = self.bump(); match token.into_token() { Token::Int(int) => Some(int), _ => unreachable!(), @@ -262,7 +262,7 @@ impl<'a> Parser<'a> { fn eat_bool(&mut self) -> Option { if matches!(self.token.token(), Token::Bool(..)) { - let token = self.next_token(); + let token = self.bump(); match token.into_token() { Token::Bool(bool) => Some(bool), _ => unreachable!(), @@ -274,7 +274,7 @@ impl<'a> Parser<'a> { fn eat_str(&mut self) -> Option { if matches!(self.token.token(), Token::Str(..)) { - let token = self.next_token(); + let token = self.bump(); match token.into_token() { Token::Str(string) => Some(string), _ => unreachable!(), @@ -286,7 +286,7 @@ impl<'a> Parser<'a> { fn eat_raw_str(&mut self) -> Option<(String, u8)> { if matches!(self.token.token(), Token::RawStr(..)) { - let token = self.next_token(); + let token = self.bump(); match token.into_token() { Token::RawStr(string, n) => Some((string, n)), _ => unreachable!(), @@ -298,7 +298,7 @@ impl<'a> Parser<'a> { fn eat_fmt_str(&mut self) -> Option { if matches!(self.token.token(), Token::FmtStr(..)) { - let token = self.next_token(); + let token = self.bump(); match token.into_token() { Token::FmtStr(string) => Some(string), _ => unreachable!(), @@ -310,7 +310,7 @@ impl<'a> Parser<'a> { fn eat_quote(&mut self) -> Option { if matches!(self.token.token(), Token::Quote(..)) { - let token = self.next_token(); + let token = self.bump(); match token.into_token() { Token::Quote(tokens) => Some(tokens), _ => unreachable!(), @@ -396,7 +396,7 @@ impl<'a> Parser<'a> { fn eat(&mut self, token: Token) -> bool { if self.token.token() == &token { - self.next_token(); + self.bump(); true } else { false diff --git a/compiler/noirc_frontend/src/parser/parser/arguments.rs b/compiler/noirc_frontend/src/parser/parser/arguments.rs index a4368ec32b1..64eb4b5460e 100644 --- a/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -32,7 +32,7 @@ impl<'a> Parser<'a> { 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.next_token(); + self.bump(); } self.parse_arguments().map(|arguments| CallArguments { arguments, is_macro_call }) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index baa97f91a59..6f52c785b69 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -76,8 +76,8 @@ impl<'a> Parser<'a> { /// UnaryOp = '&' 'mut' | '-' | '!' | '*' fn parse_unary_op(&mut self) -> Option { if self.tokens_follow(Token::Ampersand, Token::Keyword(Keyword::Mut)) { - self.next_token(); - self.next_token(); + self.bump(); + self.bump(); Some(UnaryOp::MutableReference) } else if self.eat(Token::Minus) { Some(UnaryOp::Minus) @@ -667,7 +667,7 @@ impl<'a> Parser<'a> { return None; } - self.next_token(); + self.bump(); self.parse_array_literal() } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 5a524ad5cf5..24310ebdaa7 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -155,7 +155,7 @@ impl<'a> Parser<'a> { self.expected_label(ParsingRuleLabel::Pattern); // Let's try with the next token - self.next_token(); + self.bump(); if self.at_eof() { return None; } else { diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 08b36e17c6a..6fa99ba4707 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -101,8 +101,8 @@ impl<'a> Parser<'a> { // 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.next_token(); - parser.next_token(); + parser.bump(); + parser.bump(); Some(BinaryOpKind::ShiftRight) } else { None diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 97772c9f437..0faa2ba80ee 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -43,7 +43,7 @@ impl<'a> Parser<'a> { self.expected_label(ParsingRuleLabel::Item); // We'll try parsing an item starting on the next token - self.next_token(); + self.bump(); continue; }; @@ -73,7 +73,7 @@ impl<'a> Parser<'a> { self.expected_label(label.clone()); // Try with the next token - self.next_token(); + self.bump(); continue; } diff --git a/compiler/noirc_frontend/src/parser/parser/lambda.rs b/compiler/noirc_frontend/src/parser/parser/lambda.rs index 811d394cf73..a6eeb428621 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -43,7 +43,7 @@ impl<'a> Parser<'a> { self.expected_label(ParsingRuleLabel::Pattern); // Let's try with the next token. - self.next_token(); + self.bump(); if self.at_eof() { return None; } else { diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index fe8ab24a441..3e2e71e1684 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -126,7 +126,7 @@ impl<'a> Parser<'a> { let generics = if allow_turbofish && self.tokens_follow(Token::DoubleColon, Token::Less) { - self.next_token(); + self.bump(); self.parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) } else { None @@ -138,7 +138,7 @@ impl<'a> Parser<'a> { && matches!(self.next_token.token(), Token::Ident(..)) { // Skip the double colons - self.next_token(); + self.bump(); } else { if allow_trailing_double_colon && self.eat_double_colon() { self.expected_identifier(); diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 2b144d16bfe..8446348d995 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -67,8 +67,8 @@ impl<'a> Parser<'a> { } if self.tokens_follow(Token::Ampersand, Token::Keyword(Keyword::Mut)) { - self.next_token(); - self.next_token(); + self.bump(); + self.bump(); if !self.next_is_colon() && self.eat_self() { return Some(PatternOrSelf::SelfPattern(SelfPattern { reference: true, diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 4fd06b95735..332cae2504b 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -31,7 +31,7 @@ impl<'a> Parser<'a> { let (semicolon_token, semicolon_span) = if self.at(Token::Semicolon) { let token = self.token.clone(); - self.next_token(); + self.bump(); let span = token.to_span(); (Some(token.into_token()), span) @@ -51,7 +51,7 @@ impl<'a> Parser<'a> { if semicolon_token.is_some() || self.at(Token::RightBrace) || self.at_eof() { return None; } else { - self.next_token(); + self.bump(); } } } @@ -216,8 +216,8 @@ impl<'a> Parser<'a> { }; if let Some(operator) = operator { - self.next_token(); - self.next_token(); + self.bump(); + self.bump(); Some(Spanned::from(self.span_since(start_span), operator)) } else { None diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index ba66f1130d8..2360aca0e4b 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -91,7 +91,7 @@ impl<'a> Parser<'a> { } self.expected_identifier(); - self.next_token(); + self.bump(); } self.eat_or_error(Token::Colon); diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 526c5994a87..f35908909e6 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -192,7 +192,7 @@ impl<'a> Parser<'a> { 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.next_token(); + self.bump(); match self.parse_type_expression() { Ok(type_expr) => { self.eat_or_error(Token::RightParen); From 5d035f33cd8665cbf3b79cdaa2c5cbd02ba03c75 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:40:27 -0300 Subject: [PATCH 217/229] Remove `tokens_follow` --- compiler/noirc_frontend/src/parser/parser.rs | 4 ---- compiler/noirc_frontend/src/parser/parser/arguments.rs | 2 +- compiler/noirc_frontend/src/parser/parser/expression.rs | 4 ++-- compiler/noirc_frontend/src/parser/parser/infix.rs | 2 +- compiler/noirc_frontend/src/parser/parser/path.rs | 3 ++- compiler/noirc_frontend/src/parser/parser/pattern.rs | 2 +- compiler/noirc_frontend/src/parser/parser/statement.rs | 2 +- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index e136085fb36..65691d3bd31 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -427,10 +427,6 @@ impl<'a> Parser<'a> { self.next_token.token() == &token } - fn tokens_follow(&self, token: Token, next_token: Token) -> bool { - self.at(token) && self.next_is(next_token) - } - fn at_eof(&self) -> bool { self.token.token() == &Token::EOF } diff --git a/compiler/noirc_frontend/src/parser/parser/arguments.rs b/compiler/noirc_frontend/src/parser/parser/arguments.rs index 64eb4b5460e..380f42809a6 100644 --- a/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -27,7 +27,7 @@ impl<'a> Parser<'a> { /// CallArguments = '!'? Arguments pub(super) fn parse_call_arguments(&mut self) -> Option { - let is_macro_call = self.tokens_follow(Token::Bang, Token::LeftParen); + 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 diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 6f52c785b69..bb6c0306ec5 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -75,7 +75,7 @@ impl<'a> Parser<'a> { /// UnaryOp = '&' 'mut' | '-' | '!' | '*' fn parse_unary_op(&mut self) -> Option { - if self.tokens_follow(Token::Ampersand, Token::Keyword(Keyword::Mut)) { + if self.at(Token::Ampersand) && self.next_is(Token::Keyword(Keyword::Mut)) { self.bump(); self.bump(); Some(UnaryOp::MutableReference) @@ -663,7 +663,7 @@ impl<'a> Parser<'a> { /// SliceExpression = '&' ArrayLiteral fn parse_slice_literal(&mut self) -> Option { - if !self.tokens_follow(Token::Ampersand, Token::LeftBracket) { + if !(self.at(Token::Ampersand) && self.next_is(Token::LeftBracket)) { return None; } diff --git a/compiler/noirc_frontend/src/parser/parser/infix.rs b/compiler/noirc_frontend/src/parser/parser/infix.rs index 6fa99ba4707..f006923b8a2 100644 --- a/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { 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.tokens_follow(Token::Greater, Token::Greater) { + } 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. diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 3e2e71e1684..015568975f3 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -124,7 +124,8 @@ impl<'a> Parser<'a> { let span = ident.span(); let generics = if allow_turbofish - && self.tokens_follow(Token::DoubleColon, Token::Less) + && self.at(Token::DoubleColon) + && self.next_is(Token::Less) { self.bump(); self.parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index 8446348d995..e09f9c2d490 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -66,7 +66,7 @@ impl<'a> Parser<'a> { } } - if self.tokens_follow(Token::Ampersand, Token::Keyword(Keyword::Mut)) { + if self.at(Token::Ampersand) && self.next_is(Token::Keyword(Keyword::Mut)) { self.bump(); self.bump(); if !self.next_is_colon() && self.eat_self() { diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 332cae2504b..8079f72ca71 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -208,7 +208,7 @@ impl<'a> Parser<'a> { Token::Pipe => Some(BinaryOpKind::Or), _ => None, } - } else if self.tokens_follow(Token::Greater, Token::GreaterEqual) { + } else if self.at(Token::Greater) && self.next_is(Token::GreaterEqual) { // >>= Some(BinaryOpKind::ShiftRight) } else { From c50fad46b9058700567be3661f9743f161725be5 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:45:44 -0300 Subject: [PATCH 218/229] Update compiler/noirc_frontend/src/parser/parser/generics.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/parser/parser/generics.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 89793834d3e..59398afe67e 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -58,11 +58,7 @@ impl<'a> Parser<'a> { /// VariableGeneric = identifier fn parse_variable_generic(&mut self) -> Option { - if let Some(ident) = self.eat_ident() { - return Some(UnresolvedGeneric::Variable(ident)); - } - - None + self.eat_ident().map(UnresolvedGeneric::Variable) } /// NumericGeneric = 'let' identifier ':' Type From 00fdb18ff8ab6b39b773ae6428da93a62c099178 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:48:48 -0300 Subject: [PATCH 219/229] Update compiler/noirc_frontend/src/parser/parser/generics.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/parser/parser/generics.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 59398afe67e..0d50096543d 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -67,9 +67,7 @@ impl<'a> Parser<'a> { return None; } - let Some(ident) = self.eat_ident() else { - return None; - }; + let ident = self.eat_ident()?; if !self.eat_colon() { self.push_error( From 458dae1528cddc28af53cc1336e1020f45c449f2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:49:15 -0300 Subject: [PATCH 220/229] Update compiler/noirc_frontend/src/parser/parser/generics.rs Co-authored-by: jfecher --- .../noirc_frontend/src/parser/parser/generics.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 0d50096543d..46951388e98 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -91,16 +91,13 @@ impl<'a> Parser<'a> { /// ResolvedGeneric = quoted_type fn parse_resolved_generic(&mut self) -> Option { - if let Some(token) = self.eat_kind(TokenKind::QuotedType) { - match token.into_token() { - Token::QuotedType(id) => { - return Some(UnresolvedGeneric::Resolved(id, self.previous_token_span)); - } - _ => unreachable!(), + let token = self.eat_kind(TokenKind::QuotedType)?; + match token.into_token() { + Token::QuotedType(id) => { + Some(UnresolvedGeneric::Resolved(id, self.previous_token_span)) } + _ => unreachable!(), } - - None } /// GenericTypeArgs = ( '<' GenericTypeArgsList? '>' ) From 26d0c85f0afb6b0837d6966f30016833254a1b06 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:50:22 -0300 Subject: [PATCH 221/229] Update compiler/noirc_frontend/src/parser/parser/generics.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/parser/parser/generics.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 46951388e98..73412666d43 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -148,8 +148,7 @@ impl<'a> Parser<'a> { } // Otherwise - let typ = self.parse_type_or_type_expression(); - let Some(typ) = typ else { + let Some(typ) = self.parse_type_or_type_expression() else { self.expected_label(ParsingRuleLabel::TypeOrTypeExpression); return None; }; From b1857b050b55d711d82bb1b4a6cc991d727c9ca4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:52:38 -0300 Subject: [PATCH 222/229] Don't use default span in one place --- .../noirc_frontend/src/parser/parser/generics.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 73412666d43..6f7fbed4f98 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -1,5 +1,3 @@ -use noirc_errors::Span; - use crate::{ ast::{ GenericTypeArg, GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, @@ -70,11 +68,16 @@ impl<'a> Parser<'a> { 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, ); - return Some(UnresolvedGeneric::Numeric { ident, typ: type_u32() }); + 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(); @@ -157,13 +160,6 @@ impl<'a> Parser<'a> { } } -fn type_u32() -> UnresolvedType { - UnresolvedType { - typ: UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), - span: Span::default(), - } -} - #[cfg(test)] mod tests { use crate::{ From f7fcbfa5095a7fabac603bcc1532e32b7f1bf2cf Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 11:57:05 -0300 Subject: [PATCH 223/229] Add a comment about using `pop` --- compiler/noirc_frontend/src/parser/parser.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 65691d3bd31..f6a34f5695c 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -80,7 +80,11 @@ impl<'a> TokenStream<'a> { fn next(&mut self) -> Option { match self { TokenStream::Lexer(lexer) => lexer.next(), - TokenStream::Tokens(tokens) => tokens.0.pop().map(Ok), + 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) + } } } } From 46aa738a64ea8371e448d2aba0c87c49a2fe341a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 12:33:31 -0300 Subject: [PATCH 224/229] Clarify comment --- compiler/noirc_frontend/src/parser/parser/parse_many.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/parse_many.rs b/compiler/noirc_frontend/src/parser/parser/parse_many.rs index 04d3f34766c..ea4dfe97122 100644 --- a/compiler/noirc_frontend/src/parser/parser/parse_many.rs +++ b/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -4,8 +4,8 @@ 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 an item. If no item is parsed, `f` must report an error - /// and return `None`. + /// 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, From 256294e23f4d93a30e247fab2986ebec65b90fea Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 12:46:39 -0300 Subject: [PATCH 225/229] Use parse_many in three more places --- .../src/parser/parser/attributes.rs | 19 ++++----- .../src/parser/parser/doc_comments.rs | 42 ++++++++----------- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 39a686d4d2c..d0f7221a6be 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -4,6 +4,7 @@ use crate::parser::ParserErrorReason; use crate::token::SecondaryAttribute; use crate::token::{Attribute, Token, TokenKind}; +use super::parse_many::without_separator; use super::Parser; impl<'a> Parser<'a> { @@ -18,18 +19,14 @@ impl<'a> Parser<'a> { /// Attributes = attribute* pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Span)> { - let mut attributes = Vec::new(); - - while let Some(token) = self.eat_kind(TokenKind::Attribute) { - match token.into_token() { - Token::Attribute(attribute) => { - attributes.push((attribute, self.previous_token_span)); - } - _ => unreachable!(), - } - } + self.parse_many("attributes", without_separator(), Self::parse_attribute) + } - attributes + 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!(), + }) } pub(super) fn validate_secondary_attributes( diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 8dd55795964..578a49641f6 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -1,40 +1,32 @@ use crate::token::{DocStyle, Token, TokenKind}; -use super::Parser; +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 { - let mut comments: Vec = Vec::new(); - - while let Some(token) = self.eat_kind(TokenKind::InnerDocComment) { - match token.into_token() { - Token::LineComment(comment, Some(DocStyle::Inner)) - | Token::BlockComment(comment, Some(DocStyle::Inner)) => { - comments.push(comment); - } - _ => unreachable!(), - } - } + self.parse_many("inner doc comments", without_separator(), Self::parse_inner_doc_comment) + } - comments + 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!(), + }) } /// OuterDocComments = outer_doc_comments* pub(super) fn parse_outer_doc_comments(&mut self) -> Vec { - let mut comments: Vec = Vec::new(); - - while let Some(token) = self.eat_kind(TokenKind::OuterDocComment) { - match token.into_token() { - Token::LineComment(comment, Some(DocStyle::Outer)) - | Token::BlockComment(comment, Some(DocStyle::Outer)) => { - comments.push(comment); - } - _ => unreachable!(), - } - } + self.parse_many("outer doc comments", without_separator(), Self::parse_outer_doc_comment) + } - comments + 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!(), + }) } } From 3effe9f419632d04d81311d7664f6f55d9d3aace Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 13:24:31 -0300 Subject: [PATCH 226/229] Remove a lot of code duplication from tests --- .../src/parser/parser/expression.rs | 263 ++++-------------- .../src/parser/parser/function.rs | 75 ++--- .../src/parser/parser/generics.rs | 44 +-- .../src/parser/parser/global.rs | 47 ++-- .../noirc_frontend/src/parser/parser/impls.rs | 136 +++------ .../noirc_frontend/src/parser/parser/path.rs | 33 +-- .../src/parser/parser/pattern.rs | 23 +- .../src/parser/parser/statement.rs | 84 ++---- .../src/parser/parser/structs.rs | 43 +-- .../src/parser/parser/traits.rs | 67 ++--- .../src/parser/parser/type_alias.rs | 27 +- .../src/parser/parser/type_expression.rs | 72 ++--- .../noirc_frontend/src/parser/parser/types.rs | 93 ++----- .../src/parser/parser/use_tree.rs | 103 ++----- .../src/parser/parser/where_clause.rs | 20 +- 15 files changed, 354 insertions(+), 776 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index bb6c0306ec5..e2b942faebf 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -761,8 +761,8 @@ mod tests { use crate::{ ast::{ - ArrayLiteral, BinaryOpKind, ExpressionKind, Literal, StatementKind, UnaryOp, - UnresolvedTypeData, + ArrayLiteral, BinaryOpKind, Expression, ExpressionKind, Literal, StatementKind, + UnaryOp, UnresolvedTypeData, }, parser::{ parser::tests::{ @@ -774,27 +774,29 @@ mod tests { token::Token, }; - #[test] - fn parses_bool_literals() { - let src = "true"; + 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 = Parser::for_str(src).parse_expression_or_error(); + 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 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); }; @@ -805,10 +807,7 @@ mod tests { #[test] fn parses_negative_integer_literal() { let src = "-42"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { panic!("Expected integer literal"); }; @@ -819,10 +818,7 @@ mod tests { #[test] fn parses_parenthesized_expression() { let src = "(42)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); }; @@ -836,20 +832,14 @@ mod tests { #[test] fn parses_unit() { let src = "()"; - 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); + let expr = parse_expression_no_errors(src); assert!(matches!(expr.kind, ExpressionKind::Literal(Literal::Unit))); } #[test] fn parses_str() { let src = "\"hello\""; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Str(string)) = expr.kind else { panic!("Expected string literal"); }; @@ -859,10 +849,7 @@ mod tests { #[test] fn parses_raw_str() { let src = "r#\"hello\"#"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::RawStr(string, n)) = expr.kind else { panic!("Expected raw string literal"); }; @@ -873,10 +860,7 @@ mod tests { #[test] fn parses_fmt_str() { let src = "f\"hello\""; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::FmtStr(string)) = expr.kind else { panic!("Expected format string literal"); }; @@ -886,10 +870,7 @@ mod tests { #[test] fn parses_tuple_expression() { let src = "(1, 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Tuple(mut exprs) = expr.kind else { panic!("Expected tuple expression"); }; @@ -913,10 +894,7 @@ mod tests { #[test] fn parses_block_expression_with_a_single_expression() { let src = "{ 1 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Block(mut block) = expr.kind else { panic!("Expected block expression"); }; @@ -942,10 +920,7 @@ mod tests { let y = 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()); - expect_no_errors(&parser.errors); + let expr = parse_expression_no_errors(src); let ExpressionKind::Block(block) = expr.kind else { panic!("Expected block expression"); }; @@ -984,10 +959,7 @@ mod tests { #[test] fn parses_unsafe_expression() { let src = "unsafe { 1 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Unsafe(block, _) = expr.kind else { panic!("Expected unsafe expression"); }; @@ -1027,10 +999,7 @@ mod tests { #[test] fn parses_empty_array_expression() { let src = "[]"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected array literal"); @@ -1041,10 +1010,7 @@ mod tests { #[test] fn parses_array_expression_with_one_element() { let src = "[1]"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected array literal"); @@ -1056,10 +1022,7 @@ mod tests { #[test] fn parses_array_expression_with_two_elements() { let src = "[1, 3]"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected array literal"); @@ -1099,10 +1062,7 @@ mod tests { #[test] fn parses_repeated_array_expression() { let src = "[1; 10]"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { repeated_element, length, @@ -1117,10 +1077,7 @@ mod tests { #[test] fn parses_empty_slice_expression() { let src = "&[]"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr.kind else { panic!("Expected slice literal"); @@ -1131,10 +1088,7 @@ mod tests { #[test] fn parses_variable_ident() { let src = "foo"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); }; @@ -1144,10 +1098,7 @@ mod tests { #[test] fn parses_variable_path() { let src = "foo::bar"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Variable(path) = expr.kind else { panic!("Expected variable"); }; @@ -1157,20 +1108,13 @@ mod tests { #[test] fn parses_variable_path_with_turbofish() { let src = "foo::<9>"; - 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!(expr.span.end() as usize, src.len()); - expect_no_errors(&parser.errors); + parse_expression_no_errors(src); } #[test] fn parses_mutable_ref() { let src = "&mut foo"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1185,10 +1129,7 @@ mod tests { #[test] fn parses_minus() { let src = "-foo"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1203,10 +1144,7 @@ mod tests { #[test] fn parses_not() { let src = "!foo"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1221,10 +1159,7 @@ mod tests { #[test] fn parses_dereference() { let src = "*foo"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Prefix(prefix) = expr.kind else { panic!("Expected prefix expression"); }; @@ -1239,10 +1174,7 @@ mod tests { #[test] fn parses_quote() { let src = "quote { 1 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Quote(tokens) = expr.kind else { panic!("Expected quote expression"); }; @@ -1252,10 +1184,7 @@ mod tests { #[test] fn parses_call() { let src = "foo(1, 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); }; @@ -1300,10 +1229,7 @@ mod tests { #[test] fn parses_call_with_turbofish() { let src = "foo::(1, 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); }; @@ -1315,10 +1241,7 @@ mod tests { #[test] fn parses_macro_call() { let src = "foo!(1, 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Call(call) = expr.kind else { panic!("Expected call expression"); }; @@ -1330,10 +1253,7 @@ mod tests { #[test] fn parses_member_access() { let src = "foo.bar"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::MemberAccess(member_access) = expr.kind else { panic!("Expected member access expression"); }; @@ -1344,10 +1264,7 @@ mod tests { #[test] fn parses_method_call() { let src = "foo.bar(1, 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); }; @@ -1361,10 +1278,7 @@ mod tests { #[test] fn parses_method_call_with_turbofish() { let src = "foo.bar::(1, 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); }; @@ -1378,10 +1292,7 @@ mod tests { #[test] fn parses_method_macro_call() { let src = "foo.bar!(1, 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::MethodCall(method_call) = expr.kind else { panic!("Expected method call expression"); }; @@ -1395,10 +1306,7 @@ mod tests { #[test] fn parses_empty_constructor() { let src = "Foo {}"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Constructor(constructor) = expr.kind else { panic!("Expected constructor"); }; @@ -1409,10 +1317,7 @@ mod tests { #[test] fn parses_constructor_with_fields() { let src = "Foo { x: 1, y, z: 2 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Constructor(mut constructor) = expr.kind else { panic!("Expected constructor"); }; @@ -1435,10 +1340,7 @@ mod tests { #[test] fn parses_parses_if_true() { let src = "if true { 1 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1454,10 +1356,7 @@ mod tests { #[test] fn parses_parses_if_var() { let src = "if foo { 1 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1467,10 +1366,7 @@ mod tests { #[test] fn parses_parses_if_else() { let src = "if true { 1 } else { 2 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1481,10 +1377,7 @@ mod tests { #[test] fn parses_parses_if_else_if() { let src = "if true { 1 } else if false { 2 } else { 3 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::If(if_expr) = expr.kind else { panic!("Expected if"); }; @@ -1497,10 +1390,7 @@ mod tests { #[test] fn parses_cast() { let src = "1 as u8"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Cast(cast_expr) = expr.kind else { panic!("Expected cast"); }; @@ -1524,10 +1414,7 @@ mod tests { #[test] fn parses_index() { let src = "1[2]"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Index(index_expr) = expr.kind else { panic!("Expected index"); }; @@ -1585,10 +1472,7 @@ mod tests { let src = &equal_or_not_equal; let expected_src = expected_equal_or_not_equal; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Infix(infix_expr) = expr.kind else { panic!("Expected infix"); }; @@ -1598,10 +1482,7 @@ mod tests { #[test] fn parses_empty_lambda() { let src = "|| 1"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Lambda(lambda) = expr.kind else { panic!("Expected lambda"); }; @@ -1613,10 +1494,7 @@ mod tests { #[test] fn parses_lambda_with_arguments() { let src = "|x, y: Field| 1"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Lambda(mut lambda) = expr.kind else { panic!("Expected lambda"); }; @@ -1634,10 +1512,7 @@ mod tests { #[test] fn parses_lambda_with_return_type() { let src = "|| -> Field 1"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Lambda(lambda) = expr.kind else { panic!("Expected lambda"); }; @@ -1649,10 +1524,7 @@ mod tests { #[test] fn parses_as_trait_path() { let src = "::baz"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::AsTraitPath(as_trait_path) = expr.kind else { panic!("Expected as_trait_path") }; @@ -1665,10 +1537,7 @@ mod tests { #[test] fn parses_comptime_expression() { let src = "comptime { 1 }"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Comptime(block, _) = expr.kind else { panic!("Expected comptime block"); }; @@ -1678,10 +1547,7 @@ mod tests { #[test] fn parses_type_path() { let src = "Field::foo"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::TypePath(type_path) = expr.kind else { panic!("Expected type_path"); }; @@ -1693,10 +1559,7 @@ mod tests { #[test] fn parses_type_path_with_generics() { let src = "Field::foo::"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::TypePath(type_path) = expr.kind else { panic!("Expected type_path"); }; @@ -1708,10 +1571,7 @@ mod tests { #[test] fn parses_unquote_var() { let src = "$foo::bar"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Unquote(expr) = expr.kind else { panic!("Expected unquote"); }; @@ -1724,10 +1584,7 @@ mod tests { #[test] fn parses_unquote_expr() { let src = "$(1 + 2)"; - 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); + let expr = parse_expression_no_errors(src); let ExpressionKind::Unquote(expr) = expr.kind else { panic!("Expected unquote"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 24310ebdaa7..6d1fc611767 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -285,7 +285,7 @@ fn empty_body() -> BlockExpression { #[cfg(test)] mod tests { use crate::{ - ast::{UnresolvedTypeData, Visibility}, + ast::{NoirFunction, UnresolvedTypeData, Visibility}, parser::{ parser::{ parse_program, @@ -298,16 +298,21 @@ mod tests { }, }; - #[test] - fn parse_simple_function() { - let src = "fn foo() {}"; - let (module, errors) = parse_program(src); + 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[0]; - let ItemKind::Function(noir_function) = &item.kind else { + let item = module.items.remove(0); + let ItemKind::Function(noir_function) = item.kind else { panic!("Expected function"); }; + noir_function + } + + #[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()); @@ -316,26 +321,14 @@ mod tests { #[test] fn parse_function_with_generics() { let src = "fn foo() {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Function(noir_function) = &item.kind else { - panic!("Expected function"); - }; + let noir_function = parse_function_no_error(src); assert_eq!(noir_function.def.generics.len(), 1); } #[test] fn parse_function_with_arguments() { let src = "fn foo(x: Field, y: Field) {}"; - 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(mut noir_function) = item.kind else { - panic!("Expected function"); - }; + let mut noir_function = parse_function_no_error(src); assert_eq!(noir_function.def.parameters.len(), 2); let param = noir_function.def.parameters.remove(0); @@ -352,13 +345,7 @@ mod tests { #[test] fn parse_function_with_argument_pub_visibility() { let src = "fn foo(x: pub Field) {}"; - 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(mut noir_function) = item.kind else { - panic!("Expected function"); - }; + 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); @@ -370,13 +357,7 @@ mod tests { #[test] fn parse_function_with_argument_return_data_visibility() { let src = "fn foo(x: return_data Field) {}"; - 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(mut noir_function) = item.kind else { - panic!("Expected function"); - }; + 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); @@ -386,13 +367,7 @@ mod tests { #[test] fn parse_function_with_argument_call_data_visibility() { let src = "fn foo(x: call_data(42) Field) {}"; - 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(mut noir_function) = item.kind else { - panic!("Expected function"); - }; + 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); @@ -402,13 +377,7 @@ mod tests { #[test] fn parse_function_return_type() { let src = "fn foo() -> Field {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Function(noir_function) = &item.kind else { - panic!("Expected function"); - }; + 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); } @@ -416,13 +385,7 @@ mod tests { #[test] fn parse_function_return_visibility() { let src = "fn foo() -> pub Field {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Function(noir_function) = &item.kind else { - panic!("Expected function"); - }; + 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); } diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 6f7fbed4f98..2c8ba5a2a65 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -163,7 +163,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + ast::{GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, @@ -172,19 +172,31 @@ mod tests { }, }; + 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 = Parser::for_str(src).parse_generics(); + let generics = parse_generics_no_errors(src); assert!(generics.is_empty()); } #[test] fn parses_generics() { let src = ""; - let mut parser = Parser::for_str(src); - let mut generics = parser.parse_generics(); - expect_no_errors(&parser.errors); + let mut generics = parse_generics_no_errors(src); assert_eq!(generics.len(), 2); let generic = generics.remove(0); @@ -207,18 +219,14 @@ mod tests { #[test] fn parses_no_generic_type_args() { let src = "1"; - let mut parser = Parser::for_str(src); - let generics = parser.parse_generic_type_args(); - expect_no_errors(&parser.errors); + let generics = parse_generic_type_args_no_errors(src); assert!(generics.is_empty()); } #[test] fn parses_generic_type_args() { let src = ""; - let mut parser = Parser::for_str(src); - let generics = parser.parse_generic_type_args(); - expect_no_errors(&parser.errors); + 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"); @@ -230,9 +238,7 @@ mod tests { #[test] fn parses_generic_type_arg_that_is_a_path() { let src = ""; - let mut parser = Parser::for_str(src); - let generics = parser.parse_generic_type_args(); - expect_no_errors(&parser.errors); + 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"); @@ -242,9 +248,7 @@ mod tests { #[test] fn parses_generic_type_arg_that_is_an_int() { let src = "<1>"; - let mut parser = Parser::for_str(src); - let generics = parser.parse_generic_type_args(); - expect_no_errors(&parser.errors); + 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"); @@ -266,16 +270,14 @@ mod tests { #[test] fn parse_arithmetic_generic_on_variable() { let src = ""; - let mut parser = Parser::for_str(src); - let generics = parser.parse_generic_type_args(); + 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 mut parser = Parser::for_str(src); - let generics = parser.parse_generic_type_args(); + 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 index a10ad71a2c5..2ea6457dc0b 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -68,7 +68,9 @@ fn ident_to_pattern(ident: Ident, mutable: bool) -> Pattern { #[cfg(test)] mod tests { use crate::{ - ast::{IntegerBitSize, ItemVisibility, Pattern, Signedness, UnresolvedTypeData}, + ast::{ + IntegerBitSize, ItemVisibility, LetStatement, Pattern, Signedness, UnresolvedTypeData, + }, parser::{ parser::{ parse_program, @@ -81,35 +83,34 @@ mod tests { }, }; - #[test] - fn parse_global_no_type_annotation() { - let src = "global foo = 1;"; - let (module, errors) = parse_program(src); + 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[0]; - let ItemKind::Global(let_statement, visibility) = &item.kind else { + 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); + assert_eq!(visibility, ItemVisibility::Private); } #[test] fn parse_global_with_type_annotation() { let src = "global foo: i32 = 1;"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Global(let_statement, _) = &item.kind else { - panic!("Expected global"); - }; + let (let_statement, _visibility) = parse_global_no_errors(src); let Pattern::Identifier(name) = &let_statement.pattern else { panic!("Expected identifier pattern"); }; @@ -123,26 +124,14 @@ mod tests { #[test] fn parse_comptime_global() { let src = "comptime global foo: i32 = 1;"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Global(let_statement, _) = &item.kind else { - panic!("Expected global"); - }; + 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 (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Global(let_statement, _) = &item.kind else { - panic!("Expected global"); - }; + let (let_statement, _visibility) = parse_global_no_errors(src); let Pattern::Mutable(pattern, _, _) = &let_statement.pattern else { panic!("Expected mutable pattern"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 8829193d471..460c7bded15 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -258,7 +258,9 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, Pattern, TraitImplItemKind, UnresolvedTypeData}, + ast::{ + ItemVisibility, NoirTraitImpl, Pattern, TraitImplItemKind, TypeImpl, UnresolvedTypeData, + }, parser::{ parser::{ parse_program, @@ -268,16 +270,32 @@ mod tests { }, }; - #[test] - fn parse_empty_impl() { - let src = "impl Foo {}"; - let (module, errors) = parse_program(src); + 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[0]; - let ItemKind::Impl(type_impl) = &item.kind else { + 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()); @@ -286,13 +304,7 @@ mod tests { #[test] fn parse_empty_impl_with_generics() { let src = "impl Foo {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Impl(type_impl) = &item.kind else { - panic!("Expected type impl"); - }; + 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()); @@ -301,13 +313,7 @@ mod tests { #[test] fn parse_impl_with_methods() { let src = "impl Foo { unconstrained fn foo() {} pub comptime fn bar() {} }"; - 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(mut type_impl) = item.kind else { - panic!("Expected type impl"); - }; + 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); @@ -333,13 +339,7 @@ mod tests { fn foo(self) {} } "; - 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"); - }; + let type_impl = parse_type_impl_no_errors(src); let attributes = type_impl.methods[0].0.item.attributes(); assert_eq!(attributes.secondary.len(), 1); } @@ -347,13 +347,7 @@ mod tests { #[test] fn parse_impl_with_self_argument() { let src = "impl Foo { fn foo(self) {} }"; - 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(mut type_impl) = item.kind else { - panic!("Expected type impl"); - }; + let mut type_impl = parse_type_impl_no_errors(src); assert_eq!(type_impl.methods.len(), 1); let (method, _) = type_impl.methods.remove(0); @@ -372,13 +366,7 @@ mod tests { #[test] fn parse_impl_with_mut_self_argument() { let src = "impl Foo { fn foo(mut self) {} }"; - 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(mut type_impl) = item.kind else { - panic!("Expected type impl"); - }; + let mut type_impl = parse_type_impl_no_errors(src); assert_eq!(type_impl.methods.len(), 1); let (method, _) = type_impl.methods.remove(0); @@ -401,13 +389,7 @@ mod tests { #[test] fn parse_impl_with_reference_mut_self_argument() { let src = "impl Foo { fn foo(&mut self) {} }"; - 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(mut type_impl) = item.kind else { - panic!("Expected type impl"); - }; + let mut type_impl = parse_type_impl_no_errors(src); assert_eq!(type_impl.methods.len(), 1); let (method, _) = type_impl.methods.remove(0); @@ -426,13 +408,7 @@ mod tests { #[test] fn parse_impl_with_self_argument_followed_by_type() { let src = "impl Foo { fn foo(self: Foo) {} }"; - 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(mut type_impl) = item.kind else { - panic!("Expected type impl"); - }; + let mut type_impl = parse_type_impl_no_errors(src); assert_eq!(type_impl.methods.len(), 1); let (method, _) = type_impl.methods.remove(0); @@ -478,13 +454,7 @@ mod tests { #[test] fn parse_empty_trait_impl() { let src = "impl Foo for Field {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::TraitImpl(trait_impl) = &item.kind else { - panic!("Expected trait impl"); - }; + 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()); @@ -494,13 +464,7 @@ mod tests { #[test] fn parse_empty_trait_impl_with_generics() { let src = "impl Foo for Field {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::TraitImpl(trait_impl) = &item.kind else { - panic!("Expected trait impl"); - }; + 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()); @@ -510,13 +474,7 @@ mod tests { #[test] fn parse_trait_impl_with_function() { let src = "impl Foo for Field { fn foo() {} }"; - 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(mut trait_impl) = item.kind else { - panic!("Expected trait impl"); - }; + 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); @@ -531,13 +489,7 @@ mod tests { #[test] fn parse_trait_impl_with_generic_type_args() { let src = "impl Foo for Field { }"; - 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(trait_impl) = item.kind else { - panic!("Expected trait impl"); - }; + 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()); } @@ -545,13 +497,7 @@ mod tests { #[test] fn parse_trait_impl_with_type() { let src = "impl Foo for Field { type Foo = i32; }"; - 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(mut trait_impl) = item.kind else { - panic!("Expected trait impl"); - }; + 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); @@ -566,13 +512,7 @@ mod tests { #[test] fn parse_trait_impl_with_let() { let src = "impl Foo for Field { let x: Field = 1; }"; - 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(mut trait_impl) = item.kind else { - panic!("Expected trait impl"); - }; + 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); diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 015568975f3..99aedc6df89 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -219,19 +219,24 @@ impl<'a> Parser<'a> { mod tests { use crate::{ - ast::PathKind, + ast::{Path, PathKind}, parser::{ parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, }, }; - #[test] - fn parses_plain_one_segment() { - let src = "foo"; + 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 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"); @@ -241,9 +246,7 @@ mod tests { #[test] fn parses_plain_two_segments() { let src = "foo::bar"; - let mut parser = Parser::for_str(src); - let path = parser.parse_path_or_error(); - expect_no_errors(&parser.errors); + 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"); @@ -255,9 +258,7 @@ mod tests { #[test] fn parses_crate_two_segments() { let src = "crate::foo::bar"; - let mut parser = Parser::for_str(src); - let path = parser.parse_path_or_error(); - expect_no_errors(&parser.errors); + 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"); @@ -269,9 +270,7 @@ mod tests { #[test] fn parses_super_two_segments() { let src = "super::foo::bar"; - let mut parser = Parser::for_str(src); - let path = parser.parse_path_or_error(); - expect_no_errors(&parser.errors); + 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"); @@ -283,9 +282,7 @@ mod tests { #[test] fn parses_dep_two_segments() { let src = "dep::foo::bar"; - let mut parser = Parser::for_str(src); - let path = parser.parse_path_or_error(); - expect_no_errors(&parser.errors); + 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"); @@ -310,9 +307,7 @@ mod tests { #[test] fn parses_with_turbofish() { let src = "foo::::bar"; - let mut parser = Parser::for_str(src); - let mut path = parser.parse_path_or_error(); - expect_no_errors(&parser.errors); + 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.to_string(), "foo"); diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index e09f9c2d490..a10fe18fd79 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -259,12 +259,17 @@ mod tests { token::{Keyword, Token}, }; - #[test] - fn parses_identifier_pattern() { - let src = "foo"; + 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"); } @@ -272,9 +277,7 @@ mod tests { #[test] fn parses_mutable_pattern() { let src = "mut foo"; - let mut parser = Parser::for_str(src); - let pattern = parser.parse_pattern_or_error(); - expect_no_errors(&parser.errors); + 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") }; @@ -284,9 +287,7 @@ mod tests { #[test] fn parses_tuple_pattern() { 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 = parse_pattern_no_errors(src); let Pattern::Tuple(mut patterns, _) = pattern else { panic!("Expected a tuple pattern") }; assert_eq!(patterns.len(), 2); @@ -325,9 +326,7 @@ mod tests { #[test] fn parses_struct_pattern() { let src = "foo::Bar { x: one, y }"; - let mut parser = Parser::for_str(src); - let pattern = parser.parse_pattern_or_error(); - expect_no_errors(&parser.errors); + let pattern = parse_pattern_no_errors(src); let Pattern::Struct(path, mut patterns, _) = pattern else { panic!("Expected a struct pattern") }; diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 8079f72ca71..d118be5d54a 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -416,7 +416,10 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ConstrainKind, ExpressionKind, ForRange, LValue, StatementKind, UnresolvedTypeData}, + ast::{ + ConstrainKind, ExpressionKind, ForRange, LValue, Statement, StatementKind, + UnresolvedTypeData, + }, parser::{ parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, @@ -426,30 +429,31 @@ mod tests { }, }; - #[test] - fn parses_break() { - let src = "break"; + 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 mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + 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 mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -462,9 +466,7 @@ mod tests { #[test] fn parses_let_statement_with_type() { let src = "let x: Field = 1;"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -477,9 +479,7 @@ mod tests { #[test] fn parses_assert() { let src = "assert(true, \"good\")"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -490,9 +490,7 @@ mod tests { #[test] fn parses_assert_eq() { let src = "assert_eq(1, 2, \"bad\")"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -522,9 +520,7 @@ mod tests { #[test] fn parses_comptime_block() { let src = "comptime { 1 }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); }; @@ -540,9 +536,7 @@ mod tests { #[test] fn parses_comptime_let() { let src = "comptime let x = 1;"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); }; @@ -554,9 +548,7 @@ mod tests { #[test] fn parses_for_array() { let src = "for i in x { }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -570,9 +562,7 @@ mod tests { #[test] fn parses_for_range() { let src = "for i in 0..10 { }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -588,9 +578,7 @@ mod tests { #[test] fn parses_for_range_inclusive() { let src = "for i in 0..=10 { }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -606,9 +594,7 @@ mod tests { #[test] fn parses_comptime_for() { let src = "comptime for i in x { }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime"); }; @@ -622,9 +608,7 @@ mod tests { #[test] fn parses_assignment() { let src = "x = 1"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -638,9 +622,7 @@ mod tests { #[test] fn parses_assignment_with_parentheses() { let src = "(x)[0] = 1"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Assign(..) = statement.kind else { panic!("Expected assign"); }; @@ -649,9 +631,7 @@ mod tests { #[test] fn parses_op_assignment() { let src = "x += 1"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -661,9 +641,7 @@ mod tests { #[test] fn parses_op_assignment_with_shift_right() { let src = "x >>= 1"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -674,9 +652,7 @@ mod tests { fn parses_if_statement_followed_by_tuple() { // This shouldn't be parsed as a call let src = "{ if 1 { 2 } (3, 4) }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Expression(expr) = statement.kind else { panic!("Expected expr"); }; @@ -690,9 +666,7 @@ mod tests { fn parses_block_followed_by_tuple() { // This shouldn't be parsed as a call let src = "{ { 2 } (3, 4) }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - expect_no_errors(&parser.errors); + let statement = parse_statement_no_errors(src); let StatementKind::Expression(expr) = statement.kind else { panic!("Expected expr"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 2360aca0e4b..6775cf35a78 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -122,7 +122,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + ast::{IntegerBitSize, NoirStruct, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ parser::{ parse_program, @@ -135,16 +135,21 @@ mod tests { }, }; - #[test] - fn parse_empty_struct() { - let src = "struct Foo {}"; - let (module, errors) = parse_program(src); + 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[0]; - let ItemKind::Struct(noir_struct) = &item.kind else { + let item = module.items.remove(0); + let ItemKind::Struct(noir_struct) = item.kind else { panic!("Expected struct"); }; + noir_struct + } + + #[test] + 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()); @@ -153,13 +158,7 @@ mod tests { #[test] fn parse_empty_struct_followed_by_semicolon() { let src = "struct Foo;"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Struct(noir_struct) = &item.kind else { - panic!("Expected struct"); - }; + 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()); @@ -168,13 +167,7 @@ mod tests { #[test] fn parse_empty_struct_with_generics() { let src = "struct Foo {}"; - 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(mut noir_struct) = item.kind else { - panic!("Expected struct"); - }; + 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); @@ -199,13 +192,7 @@ mod tests { #[test] fn parse_struct_with_fields() { let src = "struct Foo { x: i32, y: Field }"; - 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(mut noir_struct) = item.kind else { - panic!("Expected struct"); - }; + let mut noir_struct = parse_struct_no_errors(src); assert_eq!("Foo", noir_struct.name.to_string()); assert_eq!(noir_struct.fields.len(), 2); diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index f3b6e956ccb..3bae152e75f 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -191,23 +191,28 @@ fn empty_trait( #[cfg(test)] mod tests { use crate::{ - ast::TraitItem, + ast::{NoirTrait, TraitItem}, parser::{ parser::{parse_program, tests::expect_no_errors}, ItemKind, }, }; - #[test] - fn parse_empty_trait() { - let src = "trait Foo {}"; - let (module, errors) = parse_program(src); + 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[0]; - let ItemKind::Trait(noir_trait) = &item.kind else { + let item = module.items.remove(0); + let ItemKind::Trait(noir_trait) = item.kind else { panic!("Expected trait"); }; + noir_trait + } + + #[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()); @@ -217,13 +222,7 @@ mod tests { #[test] fn parse_trait_with_generics() { let src = "trait Foo {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Trait(noir_trait) = &item.kind else { - panic!("Expected trait"); - }; + 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()); @@ -233,13 +232,7 @@ mod tests { #[test] fn parse_trait_with_where_clause() { let src = "trait Foo where A: Z {}"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Trait(noir_trait) = &item.kind else { - panic!("Expected trait"); - }; + 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); @@ -249,13 +242,7 @@ mod tests { #[test] fn parse_trait_with_type() { let src = "trait Foo { type Elem; }"; - 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(mut noir_trait) = item.kind else { - panic!("Expected trait"); - }; + let mut noir_trait = parse_trait_no_errors(src); assert_eq!(noir_trait.items.len(), 1); let item = noir_trait.items.remove(0).item; @@ -268,13 +255,7 @@ mod tests { #[test] fn parse_trait_with_constant() { let src = "trait Foo { let x: Field = 1; }"; - 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(mut noir_trait) = item.kind else { - panic!("Expected trait"); - }; + let mut noir_trait = parse_trait_no_errors(src); assert_eq!(noir_trait.items.len(), 1); let item = noir_trait.items.remove(0).item; @@ -289,13 +270,7 @@ mod tests { #[test] fn parse_trait_with_function_no_body() { let src = "trait Foo { fn foo(); }"; - 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(mut noir_trait) = item.kind else { - panic!("Expected trait"); - }; + let mut noir_trait = parse_trait_no_errors(src); assert_eq!(noir_trait.items.len(), 1); let item = noir_trait.items.remove(0).item; @@ -308,13 +283,7 @@ mod tests { #[test] fn parse_trait_with_function_with_body() { let src = "trait Foo { fn foo() {} }"; - 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(mut noir_trait) = item.kind else { - panic!("Expected trait"); - }; + let mut noir_trait = parse_trait_no_errors(src); assert_eq!(noir_trait.items.len(), 1); let item = noir_trait.items.remove(0).item; diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 05b436b59b5..52815dc3783 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -55,23 +55,28 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::UnresolvedTypeData, + ast::{NoirTypeAlias, UnresolvedTypeData}, parser::{ parser::{parse_program, tests::expect_no_errors}, ItemKind, }, }; - #[test] - fn parse_type_alias_no_generics() { - let src = "type Foo = Field;"; - let (module, errors) = parse_program(src); + 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[0]; - let ItemKind::TypeAlias(alias) = &item.kind else { + 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); @@ -80,13 +85,7 @@ mod tests { #[test] fn parse_type_alias_with_generics() { let src = "type Foo = Field;"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::TypeAlias(alias) = &item.kind else { - panic!("Expected type alias"); - }; + 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 index f35908909e6..c3f27d9d49a 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -412,7 +412,7 @@ mod tests { use core::panic; use crate::{ - ast::{UnresolvedTypeData, UnresolvedTypeExpression}, + ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::{ parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, @@ -423,12 +423,24 @@ mod tests { BinaryTypeOperator, }; - #[test] - fn parses_constant_type_expression() { - let src = "42"; + 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"); }; @@ -438,9 +450,7 @@ mod tests { #[test] fn parses_variable_type_expression() { let src = "foo::bar"; - let mut parser = Parser::for_str(src); - let expr = parser.parse_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let expr = parse_type_expression_no_errors(src); let UnresolvedTypeExpression::Variable(path) = expr else { panic!("Expected path"); }; @@ -450,9 +460,7 @@ mod tests { #[test] fn parses_binary_type_expression() { let src = "1 + 2 * 3 + 4"; - let mut parser = Parser::for_str(src); - let expr = parser.parse_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let expr = parse_type_expression_no_errors(src); let UnresolvedTypeExpression::BinaryOperation(lhs, operator, rhs, _) = expr else { panic!("Expected binary operation"); }; @@ -464,9 +472,7 @@ mod tests { #[test] fn parses_parenthesized_type_expression() { let src = "(N)"; - let mut parser = Parser::for_str(src); - let expr = parser.parse_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let expr = parse_type_expression_no_errors(src); let UnresolvedTypeExpression::Variable(path) = expr else { panic!("Expected variable"); }; @@ -476,18 +482,14 @@ mod tests { #[test] fn parses_minus_type_expression() { let src = "-N"; - let mut parser = Parser::for_str(src); - let expr = parser.parse_type_expression().unwrap(); - expect_no_errors(&parser.errors); + 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 mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression"); }; @@ -500,9 +502,7 @@ mod tests { #[test] fn parse_type_or_type_expression_variable() { let src = "foo::Bar"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { panic!("Expected named type"); }; @@ -513,9 +513,7 @@ mod tests { #[test] fn parses_type_or_type_expression_binary() { let src = "1 + 2 * 3 + 4"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression"); }; @@ -530,9 +528,7 @@ mod tests { #[test] fn parses_type_or_type_expression_minus() { let src = "-N"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression"); }; @@ -542,9 +538,7 @@ mod tests { #[test] fn parses_type_or_type_expression_unit() { let src = "()"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Unit = typ.typ else { panic!("Expected unit type"); }; @@ -553,9 +547,7 @@ mod tests { #[test] fn parses_type_or_type_expression_parenthesized_type() { 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 typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected parenthesized type"); }; @@ -567,9 +559,7 @@ mod tests { #[test] fn parses_type_or_type_expression_parenthesized_constant() { let src = "(1)"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression type"); }; @@ -579,9 +569,7 @@ mod tests { #[test] fn parses_type_or_type_expression_tuple_type() { let src = "(Field, bool)"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Tuple(types) = typ.typ else { panic!("Expected tuple type"); }; @@ -640,9 +628,7 @@ mod tests { #[test] fn parses_type_or_type_expression_var_minus_one() { let src = "N - 1"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_type_expression().unwrap(); - expect_no_errors(&parser.errors); + let typ = parse_type_or_type_expression_no_errors(src); let UnresolvedTypeData::Expression(expr) = typ.typ else { panic!("Expected expression type"); }; diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 4bfc9f248d1..6702704d32c 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -421,7 +421,7 @@ mod tests { use strum::IntoEnumIterator; use crate::{ - ast::{IntegerBitSize, Signedness, UnresolvedTypeData}, + ast::{IntegerBitSize, Signedness, UnresolvedType, UnresolvedTypeData}, parser::{ parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, @@ -429,30 +429,31 @@ mod tests { QuotedType, }; - #[test] - fn parses_unit_type() { - let src = "()"; + 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 mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); } #[test] fn parses_int_type() { let src = "u32"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); assert!(matches!( typ.typ, UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) @@ -462,18 +463,14 @@ mod tests { #[test] fn parses_field_type() { let src = "Field"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); } #[test] fn parses_str_type() { let src = "str<10>"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + 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"); } @@ -481,9 +478,7 @@ mod tests { #[test] fn parses_fmtstr_type() { let src = "fmtstr<10, T>"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::FormatString(expr, typ) = typ.typ else { panic!("Expected a format string type") }; @@ -495,9 +490,7 @@ mod tests { fn parses_comptime_types() { for quoted_type in QuotedType::iter() { let src = quoted_type.to_string(); - let mut parser = Parser::for_str(&src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(&src); let UnresolvedTypeData::Quoted(parsed_qouted_type) = typ.typ else { panic!("Expected a quoted type for {}", quoted_type) }; @@ -508,9 +501,7 @@ mod tests { #[test] fn parses_tuple_type() { let src = "(Field, bool)"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + 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); @@ -524,9 +515,7 @@ mod tests { #[test] fn parses_tuple_type_one_element() { let src = "(Field,)"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + 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); @@ -537,9 +526,7 @@ mod tests { #[test] fn parses_parenthesized_type() { let src = "(Field)"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; @@ -561,9 +548,7 @@ mod tests { #[test] fn parses_mutable_reference_type() { let src = "&mut Field"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::MutableReference(typ) = typ.typ else { panic!("Expected a mutable reference type") }; @@ -573,9 +558,7 @@ mod tests { #[test] fn parses_named_type_no_generics() { let src = "foo::Bar"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Named(path, generics, _) = typ.typ else { panic!("Expected a named type") }; @@ -586,9 +569,7 @@ mod tests { #[test] fn parses_slice_type() { let src = "[Field]"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + 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)); } @@ -609,9 +590,7 @@ mod tests { #[test] fn parses_array_type() { let src = "[Field; 10]"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Array(expr, typ) = typ.typ else { panic!("Expected an array type") }; @@ -622,9 +601,7 @@ mod tests { #[test] fn parses_empty_function_type() { let src = "fn() -> Field"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Function(args, ret, env, unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -637,9 +614,7 @@ mod tests { #[test] fn parses_function_type_with_arguments() { let src = "fn(Field, bool) -> Field"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Function(args, _ret, _env, _unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -651,9 +626,7 @@ mod tests { #[test] fn parses_function_type_with_return_type() { let src = "fn() -> Field"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Function(_args, ret, _env, _unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -663,9 +636,7 @@ mod tests { #[test] fn parses_function_type_with_env() { let src = "fn[Field]() -> Field"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Function(_args, _ret, env, _unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -675,9 +646,7 @@ mod tests { #[test] fn parses_unconstrained_function_type() { let src = "unconstrained fn() -> Field"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::Function(_args, _ret, _env, unconstrained) = typ.typ else { panic!("Expected a function type") }; @@ -687,9 +656,7 @@ mod tests { #[test] fn parses_trait_as_type_no_generics() { let src = "impl foo::Bar"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::TraitAsType(path, generics) = typ.typ else { panic!("Expected trait as type") }; @@ -700,9 +667,7 @@ mod tests { #[test] fn parses_as_trait_path() { let src = "::baz"; - let mut parser = Parser::for_str(src); - let typ = parser.parse_type_or_error(); - expect_no_errors(&parser.errors); + let typ = parse_type_no_errors(src); let UnresolvedTypeData::AsTraitPath(as_trait_path) = typ.typ else { panic!("Expected as_trait_path") }; diff --git a/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 0e90a105c97..1c43732c94f 100644 --- a/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -108,24 +108,29 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, PathKind, UseTreeKind}, + ast::{ItemVisibility, PathKind, UseTree, UseTreeKind}, parser::{ parser::{parse_program, tests::expect_no_errors}, ItemKind, }, }; - #[test] - fn parse_simple() { - let src = "use foo;"; - let (module, errors) = parse_program(src); + 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[0]; - let ItemKind::Import(use_tree, visibility) = &item.kind else { + let item = module.items.remove(0); + let ItemKind::Import(use_tree, visibility) = item.kind else { panic!("Expected import"); }; - assert_eq!(visibility, &ItemVisibility::Private); + (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 { @@ -138,39 +143,21 @@ mod tests { #[test] fn parse_simple_pub() { let src = "pub use foo;"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Import(_, visibility) = &item.kind else { - panic!("Expected import"); - }; - assert_eq!(visibility, &ItemVisibility::Public); + 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 (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Import(_, visibility) = &item.kind else { - panic!("Expected import"); - }; - assert_eq!(visibility, &ItemVisibility::PublicCrate); + 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 (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"); - }; + 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()); @@ -184,13 +171,7 @@ mod tests { #[test] fn parse_with_crate_prefix() { let src = "use crate::foo;"; - 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"); - }; + 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()); @@ -204,13 +185,7 @@ mod tests { #[test] fn parse_with_dep_prefix() { let src = "use dep::foo;"; - 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"); - }; + 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()); @@ -224,13 +199,7 @@ mod tests { #[test] fn parse_with_super_prefix() { let src = "use super::foo;"; - 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"); - }; + 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()); @@ -244,14 +213,8 @@ mod tests { #[test] fn parse_list() { let src = "use foo::{bar, baz};"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Import(use_tree, visibility) = &item.kind else { - panic!("Expected import"); - }; - assert_eq!(visibility, &ItemVisibility::Private); + 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 { @@ -263,14 +226,8 @@ mod tests { #[test] fn parse_list_trailing_comma() { let src = "use foo::{bar, baz, };"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Import(use_tree, visibility) = &item.kind else { - panic!("Expected import"); - }; - assert_eq!(visibility, &ItemVisibility::Private); + 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 { @@ -282,14 +239,8 @@ mod tests { #[test] fn parse_list_that_starts_with_crate() { let src = "use crate::{foo, bar};"; - let (module, errors) = parse_program(src); - expect_no_errors(&errors); - assert_eq!(module.items.len(), 1); - let item = &module.items[0]; - let ItemKind::Import(use_tree, visibility) = &item.kind else { - panic!("Expected import"); - }; - assert_eq!(visibility, &ItemVisibility::Private); + let (use_tree, visibility) = parse_use_tree_no_errors(src); + assert_eq!(visibility, ItemVisibility::Private); assert_eq!("crate::{foo, bar}", use_tree.to_string()); } diff --git a/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 333ab71d6f7..a753ffb6fd2 100644 --- a/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -93,6 +93,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ + ast::UnresolvedTraitConstraint, parser::{ parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, @@ -102,21 +103,24 @@ mod tests { token::Token, }; - #[test] - fn parses_no_where_clause() { - let src = "{"; + 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 parser = Parser::for_str(src); - let mut constraints = parser.parse_where_clause(); - expect_no_errors(&parser.errors); + let mut constraints = parse_where_clause_no_errors(src); assert_eq!(constraints.len(), 2); let constraint = constraints.remove(0); @@ -132,9 +136,7 @@ mod tests { #[test] fn parses_two_where_clauses() { let src = "where Foo: Bar, i32: Qux {"; - let mut parser = Parser::for_str(src); - let mut constraints = parser.parse_where_clause(); - expect_no_errors(&parser.errors); + let mut constraints = parse_where_clause_no_errors(src); assert_eq!(constraints.len(), 2); let constraint = constraints.remove(0); From 2fb31a4e127e3d7f08a27d31abf4df9be007de20 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 13:25:39 -0300 Subject: [PATCH 227/229] Apply suggestions from code review Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- compiler/noirc_frontend/src/ast/expression.rs | 3 ++- compiler/noirc_frontend/src/hir_def/types.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 318cf5850fe..64edae8322f 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -308,7 +308,8 @@ impl Expression { pub type BinaryOp = Spanned; -#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone, strum_macros::EnumIter)] +#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)] +#[cfg_attr(test, derive(strum_macros::EnumIter))] pub enum BinaryOpKind { Add, Subtract, diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index e0d9af7b983..5c2322acfda 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -243,7 +243,8 @@ impl std::fmt::Display for Kind { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord, strum_macros::EnumIter)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] +#[cfg_attr(test, derive(strum_macros::EnumIter))] pub enum QuotedType { Expr, Quoted, From cb3d321484d302efaac199872ed52e6419a18f85 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 13:38:57 -0300 Subject: [PATCH 228/229] Introduce `parse_option` and make it and `parse_result` Parser methods --- .../noirc_frontend/src/elaborator/comptime.rs | 8 +-- .../interpreter/builtin/builtin_helpers.rs | 6 +-- .../noirc_frontend/src/hir/comptime/value.rs | 8 +-- compiler/noirc_frontend/src/parser/mod.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 53 ++++++++++++------- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index eb26bea2654..426e160206f 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -260,12 +260,8 @@ impl<'context> Elaborator<'context> { return Err((lexing_errors.swap_remove(0).into(), location.file)); } - let mut parser = Parser::for_tokens(tokens); - let expression = parser.parse_expression(); - if !parser.errors.is_empty() { - return Ok(None); - } - let Some(expression) = expression else { + let Some(expression) = Parser::for_tokens(tokens).parse_option(Parser::parse_expression) + else { return Ok(None); }; 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 7407801a19e..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 @@ -7,7 +7,7 @@ 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::{parse_result, Parser}; +use crate::parser::Parser; use crate::{ ast::{ BlockExpression, ExpressionKind, Ident, IntegerBitSize, LValue, Pattern, Signedness, @@ -422,13 +422,13 @@ pub(super) fn parse_tokens<'a, T, F>( quoted: Tokens, interner: &NodeInterner, location: Location, - parser: F, + parsing_function: F, rule: &'static str, ) -> IResult where F: FnOnce(&mut Parser<'a>) -> T, { - parse_result(Parser::for_tokens(quoted), parser).map_err(|mut errors| { + 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/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index ee6497a6fe4..4b55735fcb1 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -18,7 +18,7 @@ use crate::{ ImplKind, }, node_interner::{ExprId, FuncId, NodeInterner, StmtId, StructId, TraitId, TraitImplId}, - parser::{parse_result, Item, Parser}, + parser::{Item, Parser}, token::{SpannedToken, Token, Tokens}, Kind, QuotedType, Shared, Type, TypeBindings, }; @@ -261,7 +261,7 @@ impl Value { tokens_to_parse.0.push(SpannedToken::new(Token::RightBrace, location.span)); let parser = Parser::for_tokens(tokens_to_parse); - return match parse_result(parser, Parser::parse_expression_or_error) { + return match parser.parse_result(Parser::parse_expression_or_error) { Ok(expr) => Ok(expr), Err(mut errors) => { let error = errors.swap_remove(0); @@ -546,7 +546,7 @@ pub(crate) fn unwrap_rc(rc: Rc) -> T { fn parse_tokens<'a, T, F>( tokens: Rc>, interner: &NodeInterner, - f: F, + parsing_function: F, location: Location, rule: &'static str, ) -> IResult @@ -554,7 +554,7 @@ where F: FnOnce(&mut Parser<'a>) -> T, { let parser = Parser::for_tokens(add_token_spans(tokens.clone(), location.span)); - match parse_result(parser, f) { + 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/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index 6cb396cf3cc..21c182a52cd 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -20,7 +20,7 @@ use crate::token::SecondaryAttribute; pub use errors::ParserError; pub use errors::ParserErrorReason; use noirc_errors::Span; -pub use parser::{parse_program, parse_result, Parser}; +pub use parser::{parse_program, Parser}; #[derive(Clone, Default)] pub struct SortedModule { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index f6a34f5695c..d0b0579ce24 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -51,26 +51,6 @@ pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { (program, errors) } -/// Invokes `f` with the given parser (`f` 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<'a, T, F>(mut parser: Parser<'a>, f: F) -> Result> -where - F: FnOnce(&mut Parser<'a>) -> T, -{ - let item = f(&mut parser); - if !parser.at_eof() { - parser.expected_token(Token::EOF); - return Err(parser.errors); - } - - if parser.errors.is_empty() { - Ok(item) - } else { - Err(parser.errors) - } -} - enum TokenStream<'a> { Lexer(Lexer<'a>), Tokens(Tokens), @@ -166,6 +146,39 @@ impl<'a> Parser<'a> { } } + /// 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); + } + + if self.errors.is_empty() { + Ok(item) + } else { + Err(self.errors) + } + } + + /// 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, + } + } + /// 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; From 279c8d2426892e4d868e983cc05a5b99ad3b548f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 7 Oct 2024 13:53:20 -0300 Subject: [PATCH 229/229] Remove `parse_modifier` --- .../src/parser/parser/modifiers.rs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/modifiers.rs b/compiler/noirc_frontend/src/parser/parser/modifiers.rs index 543a639cdd2..d3bf692ee53 100644 --- a/compiler/noirc_frontend/src/parser/parser/modifiers.rs +++ b/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -16,23 +16,24 @@ pub(crate) struct Modifiers { impl<'a> Parser<'a> { /// Modifiers = 'unconstrained'? ItemVisibility 'comptime'? 'mut'? pub(crate) fn parse_modifiers(&mut self, allow_mutable: bool) -> Modifiers { - let unconstrained = self.parse_modifier(Keyword::Unconstrained); + 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 = self.parse_modifier(Keyword::Comptime); - let mutable = if allow_mutable { self.parse_modifier(Keyword::Mut) } else { None }; - - Modifiers { visibility, visibility_span, unconstrained, comptime, mutable } - } - - fn parse_modifier(&mut self, keyword: Keyword) -> Option { - if self.eat_keyword(keyword) { + 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 } } }