Skip to content

Commit

Permalink
feat: Implement parsing of traits (#1886)
Browse files Browse the repository at this point in the history
* Parse traits

* Fix modifier ordering
  • Loading branch information
jfecher authored Jul 12, 2023
1 parent 6fa3144 commit 3ba1e72
Show file tree
Hide file tree
Showing 10 changed files with 386 additions and 73 deletions.
3 changes: 2 additions & 1 deletion crates/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fmt::Display;

use crate::token::{Attribute, Token};
use crate::{Ident, Path, Pattern, Recoverable, Statement, UnresolvedType};
use crate::{Ident, Path, Pattern, Recoverable, Statement, TraitConstraint, UnresolvedType};
use acvm::FieldElement;
use iter_extended::vecmap;
use noirc_errors::{Span, Spanned};
Expand Down Expand Up @@ -337,6 +337,7 @@ pub struct FunctionDefinition {
pub parameters: Vec<(Pattern, UnresolvedType, noirc_abi::AbiVisibility)>,
pub body: BlockExpression,
pub span: Span,
pub where_clause: Vec<TraitConstraint>,
pub return_type: UnresolvedType,
pub return_visibility: noirc_abi::AbiVisibility,
pub return_distinctness: noirc_abi::AbiDistinctness,
Expand Down
2 changes: 2 additions & 0 deletions crates/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ mod expression;
mod function;
mod statement;
mod structure;
mod traits;

pub use expression::*;
pub use function::*;

use noirc_errors::Span;
pub use statement::*;
pub use structure::*;
pub use traits::*;

use crate::{
parser::{ParserError, ParserErrorReason},
Expand Down
8 changes: 8 additions & 0 deletions crates/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,14 @@ impl Pattern {
_ => panic!("only the identifier pattern can return a name"),
}
}

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"),
}
}
}

impl Recoverable for Pattern {
Expand Down
29 changes: 1 addition & 28 deletions crates/noirc_frontend/src/ast/structure.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::Display;

use crate::{Ident, NoirFunction, UnresolvedGenerics, UnresolvedType};
use crate::{Ident, UnresolvedGenerics, UnresolvedType};
use iter_extended::vecmap;
use noirc_errors::Span;

Expand All @@ -24,15 +24,6 @@ impl NoirStruct {
}
}

/// Ast node for an impl
#[derive(Clone, Debug)]
pub struct NoirImpl {
pub object_type: UnresolvedType,
pub type_span: Span,
pub generics: UnresolvedGenerics,
pub methods: Vec<NoirFunction>,
}

impl Display for NoirStruct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.generics, |generic| generic.to_string());
Expand All @@ -47,21 +38,3 @@ impl Display for NoirStruct {
write!(f, "}}")
}
}

impl Display for NoirImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.generics, |generic| generic.to_string());
let generics = if generics.is_empty() { "".into() } else { generics.join(", ") };

writeln!(f, "impl{} {} {{", generics, self.object_type)?;

for method in self.methods.iter() {
let method = method.to_string();
for line in method.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}
165 changes: 165 additions & 0 deletions crates/noirc_frontend/src/ast/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use std::fmt::Display;

use iter_extended::vecmap;
use noirc_errors::Span;

use crate::{Ident, NoirFunction, UnresolvedGenerics, UnresolvedType};

/// AST node for trait definitions:
/// `trait name<generics> { ... items ... }`
#[derive(Clone, Debug)]
pub struct NoirTrait {
pub name: Ident,
pub generics: Vec<Ident>,
pub items: Vec<TraitItem>,
}

/// Any declaration inside the body of a trait that a user is required to
/// specify when implementing the trait.
#[derive(Clone, Debug)]
pub enum TraitItem {
Function {
name: Ident,
generics: Vec<Ident>,
parameters: Vec<(Ident, UnresolvedType)>,
return_type: UnresolvedType,
where_clause: Vec<TraitConstraint>,
},
Type {
name: Ident,
},
}

/// Ast node for an impl of a concrete type
/// `impl object_type<generics> { ... methods ... }`
#[derive(Clone, Debug)]
pub struct TypeImpl {
pub object_type: UnresolvedType,
pub type_span: Span,
pub generics: UnresolvedGenerics,
pub methods: Vec<NoirFunction>,
}

/// Ast node for an implementation of a trait for a particular type
/// `impl trait_name<trait_generics> for object_type where where_clauses { ... items ... }`
#[derive(Clone, Debug)]
pub struct TraitImpl {
pub impl_generics: UnresolvedGenerics,

pub trait_name: Ident,
pub trait_generics: Vec<UnresolvedType>,

pub object_type: UnresolvedType,
pub object_type_span: Span,

pub where_clause: Vec<TraitConstraint>,

pub items: Vec<TraitImplItem>,
}

/// Represents a trait constraint such as `where Foo: Display`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TraitConstraint {
pub typ: UnresolvedType,
pub trait_name: Ident,
pub trait_generics: Vec<UnresolvedType>,
}

#[derive(Clone, Debug)]
pub enum TraitImplItem {
Function(NoirFunction),
Type { name: Ident, alias: UnresolvedType },
}

impl Display for TypeImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.generics, |generic| generic.to_string());
let generics = if generics.is_empty() { "".into() } else { generics.join(", ") };

writeln!(f, "impl{} {} {{", generics, self.object_type)?;

for method in self.methods.iter() {
let method = method.to_string();
for line in method.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}

impl Display for NoirTrait {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.generics, |generic| generic.to_string());
let generics = if generics.is_empty() { "".into() } else { generics.join(", ") };

writeln!(f, "trait {}{} {{", self.name, generics)?;

for item in self.items.iter() {
let item = item.to_string();
for line in item.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}

impl Display for TraitItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TraitItem::Function { name, generics, parameters, return_type, where_clause } => {
let generics = vecmap(generics, |generic| generic.to_string());
let parameters = vecmap(parameters, |(name, typ)| format!("{name}: {typ}"));
let where_clause = vecmap(where_clause, ToString::to_string);

let generics = generics.join(", ");
let parameters = parameters.join(", ");
let where_clause = where_clause.join(", ");

write!(
f,
"fn {name}<{}>({}) -> {} where {};",
generics, parameters, return_type, where_clause
)
}
TraitItem::Type { name } => write!(f, "type {name};"),
}
}
}

impl Display for TraitConstraint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.trait_generics, |generic| generic.to_string());
write!(f, "{}: {}<{}>", self.typ, self.trait_name, generics.join(", "))
}
}

impl Display for TraitImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.trait_generics, |generic| generic.to_string());
let generics = generics.join(", ");

writeln!(f, "impl {}<{}> for {} {{", self.trait_name, generics, self.object_type)?;

for item in self.items.iter() {
let item = item.to_string();
for line in item.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}

impl Display for TraitImplItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TraitImplItem::Function(function) => function.fmt(f),
TraitImplItem::Type { name, alias } => write!(f, "type {name} = {alias}"),
}
}
}
4 changes: 2 additions & 2 deletions crates/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use noirc_errors::FileDiagnostic;

use crate::{
graph::CrateId, hir::def_collector::dc_crate::UnresolvedStruct, node_interner::StructId,
parser::SubModule, Ident, LetStatement, NoirFunction, NoirImpl, NoirStruct, ParsedModule,
parser::SubModule, Ident, LetStatement, NoirFunction, NoirStruct, ParsedModule, TypeImpl,
};

use super::{
Expand Down Expand Up @@ -92,7 +92,7 @@ impl<'a> ModCollector<'a> {
}
}

fn collect_impls(&mut self, context: &mut Context, impls: Vec<NoirImpl>) {
fn collect_impls(&mut self, context: &mut Context, impls: Vec<TypeImpl>) {
for r#impl in impls {
let mut unresolved_functions =
UnresolvedFunctions { file_id: self.file_id, functions: Vec::new() };
Expand Down
21 changes: 15 additions & 6 deletions crates/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,20 +428,23 @@ pub enum Keyword {
Fn,
For,
Global,
Impl,
If,
Impl,
In,
Internal,
Let,
Mod,
Mut,
Open,
Pub,
String,
Return,
String,
Struct,
Trait,
Type,
Unconstrained,
Use,
Where,
While,
}

Expand All @@ -463,20 +466,23 @@ impl fmt::Display for Keyword {
Keyword::Fn => write!(f, "fn"),
Keyword::For => write!(f, "for"),
Keyword::Global => write!(f, "global"),
Keyword::Impl => write!(f, "impl"),
Keyword::If => write!(f, "if"),
Keyword::Impl => write!(f, "impl"),
Keyword::In => write!(f, "in"),
Keyword::Internal => write!(f, "internal"),
Keyword::Let => write!(f, "let"),
Keyword::Mod => write!(f, "mod"),
Keyword::Mut => write!(f, "mut"),
Keyword::Open => write!(f, "open"),
Keyword::Pub => write!(f, "pub"),
Keyword::String => write!(f, "str"),
Keyword::Return => write!(f, "return"),
Keyword::String => write!(f, "str"),
Keyword::Struct => write!(f, "struct"),
Keyword::Trait => write!(f, "trait"),
Keyword::Type => write!(f, "type"),
Keyword::Unconstrained => write!(f, "unconstrained"),
Keyword::Use => write!(f, "use"),
Keyword::Where => write!(f, "where"),
Keyword::While => write!(f, "while"),
}
}
Expand All @@ -501,20 +507,23 @@ impl Keyword {
"fn" => Keyword::Fn,
"for" => Keyword::For,
"global" => Keyword::Global,
"impl" => Keyword::Impl,
"if" => Keyword::If,
"impl" => Keyword::Impl,
"in" => Keyword::In,
"internal" => Keyword::Internal,
"let" => Keyword::Let,
"mod" => Keyword::Mod,
"mut" => Keyword::Mut,
"open" => Keyword::Open,
"pub" => Keyword::Pub,
"str" => Keyword::String,
"return" => Keyword::Return,
"str" => Keyword::String,
"struct" => Keyword::Struct,
"trait" => Keyword::Trait,
"type" => Keyword::Type,
"unconstrained" => Keyword::Unconstrained,
"use" => Keyword::Use,
"where" => Keyword::Where,
"while" => Keyword::While,

"true" => return Some(Token::Bool(true)),
Expand Down
4 changes: 4 additions & 0 deletions crates/noirc_frontend/src/parser/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub enum ParserErrorReason {
InvalidArrayLengthExpression(Expression),
#[error("Early 'return' is unsupported")]
EarlyReturn,
#[error("Patterns aren't allowed in a trait's function declarations")]
PatternInTraitFunctionParameter,
#[error("Traits are an experimental feature that are not yet in a working state")]
TraitsAreExperimental,
}

/// Represents a parsing error, or a parsing error in the making.
Expand Down
Loading

0 comments on commit 3ba1e72

Please sign in to comment.