-
Notifications
You must be signed in to change notification settings - Fork 381
chore: Add Hir -> Ast conversion #4788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,350 @@ | ||
| use iter_extended::vecmap; | ||
| use noirc_errors::{Span, Spanned}; | ||
|
|
||
| use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; | ||
| use crate::hir_def::expr::{HirArrayLiteral, HirExpression, HirIdent}; | ||
| use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; | ||
| use crate::macros_api::HirLiteral; | ||
| use crate::node_interner::{ExprId, NodeInterner, StmtId}; | ||
| use crate::{ | ||
| ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, | ||
| ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, Ident, IfExpression, | ||
| IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, | ||
| MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, Type, | ||
| UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, | ||
| }; | ||
|
|
||
| // TODO: | ||
| // - Full path for idents & types | ||
| // - Assert/AssertEq information lost | ||
| // - The type name span is lost in constructor patterns & expressions | ||
| // - All type spans are lost | ||
| // - Type::TypeVariable has no equivalent in the Ast | ||
|
|
||
| impl StmtId { | ||
| #[allow(unused)] | ||
| fn to_ast(self, interner: &NodeInterner) -> Statement { | ||
| let statement = interner.statement(&self); | ||
| let span = interner.statement_span(&self); | ||
|
|
||
| let kind = match statement { | ||
| HirStatement::Let(let_stmt) => { | ||
| let pattern = let_stmt.pattern.into_ast(interner); | ||
| let r#type = interner.id_type(let_stmt.expression).to_ast(); | ||
| let expression = let_stmt.expression.to_ast(interner); | ||
| StatementKind::Let(LetStatement { | ||
| pattern, | ||
| r#type, | ||
| expression, | ||
| attributes: Vec::new(), | ||
| }) | ||
| } | ||
| HirStatement::Constrain(constrain) => { | ||
| let expr = constrain.0.to_ast(interner); | ||
| let message = constrain.2.map(|message| message.to_ast(interner)); | ||
|
|
||
| // TODO: Find difference in usage between Assert & AssertEq | ||
| StatementKind::Constrain(ConstrainStatement(expr, message, ConstrainKind::Assert)) | ||
michaeljklein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| HirStatement::Assign(assign) => StatementKind::Assign(AssignStatement { | ||
| lvalue: assign.lvalue.into_ast(interner), | ||
| expression: assign.expression.to_ast(interner), | ||
| }), | ||
| HirStatement::For(for_stmt) => StatementKind::For(ForLoopStatement { | ||
| identifier: for_stmt.identifier.to_ast(interner), | ||
| range: ForRange::Range( | ||
| for_stmt.start_range.to_ast(interner), | ||
| for_stmt.end_range.to_ast(interner), | ||
| ), | ||
| block: for_stmt.block.to_ast(interner), | ||
| span, | ||
| }), | ||
| HirStatement::Break => StatementKind::Break, | ||
| HirStatement::Continue => StatementKind::Continue, | ||
| HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)), | ||
| HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), | ||
| HirStatement::Error => StatementKind::Error, | ||
| }; | ||
|
|
||
| Statement { kind, span } | ||
| } | ||
| } | ||
|
|
||
| impl ExprId { | ||
| #[allow(unused)] | ||
| fn to_ast(self, interner: &NodeInterner) -> Expression { | ||
| let expression = interner.expression(&self); | ||
| let span = interner.expr_span(&self); | ||
|
|
||
| let kind = match expression { | ||
| HirExpression::Ident(ident) => { | ||
| let path = Path::from_ident(ident.to_ast(interner)); | ||
| ExpressionKind::Variable(path) | ||
| } | ||
| HirExpression::Literal(HirLiteral::Array(array)) => { | ||
| let array = array.into_ast(interner, span); | ||
| ExpressionKind::Literal(Literal::Array(array)) | ||
| } | ||
| HirExpression::Literal(HirLiteral::Slice(array)) => { | ||
| let array = array.into_ast(interner, span); | ||
| ExpressionKind::Literal(Literal::Slice(array)) | ||
| } | ||
| HirExpression::Literal(HirLiteral::Bool(value)) => { | ||
| ExpressionKind::Literal(Literal::Bool(value)) | ||
| } | ||
| HirExpression::Literal(HirLiteral::Integer(value, sign)) => { | ||
| ExpressionKind::Literal(Literal::Integer(value, sign)) | ||
| } | ||
| HirExpression::Literal(HirLiteral::Str(string)) => { | ||
| ExpressionKind::Literal(Literal::Str(string)) | ||
| } | ||
| HirExpression::Literal(HirLiteral::FmtStr(string, _exprs)) => { | ||
| // TODO: Is throwing away the exprs here valid? | ||
| ExpressionKind::Literal(Literal::FmtStr(string)) | ||
| } | ||
| HirExpression::Literal(HirLiteral::Unit) => ExpressionKind::Literal(Literal::Unit), | ||
| HirExpression::Block(expr) => { | ||
| let statements = vecmap(expr.statements, |statement| statement.to_ast(interner)); | ||
| ExpressionKind::Block(BlockExpression { statements }) | ||
| } | ||
| HirExpression::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { | ||
| operator: prefix.operator, | ||
| rhs: prefix.rhs.to_ast(interner), | ||
| })), | ||
| HirExpression::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { | ||
| lhs: infix.lhs.to_ast(interner), | ||
| operator: Spanned::from(infix.operator.location.span, infix.operator.kind), | ||
| rhs: infix.rhs.to_ast(interner), | ||
| })), | ||
| HirExpression::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { | ||
| collection: index.collection.to_ast(interner), | ||
| index: index.index.to_ast(interner), | ||
| })), | ||
| HirExpression::Constructor(constructor) => { | ||
| let type_name = constructor.r#type.borrow().name.to_string(); | ||
| let type_name = Path::from_single(type_name, span); | ||
| let fields = | ||
| vecmap(constructor.fields, |(name, expr)| (name, expr.to_ast(interner))); | ||
|
|
||
| ExpressionKind::Constructor(Box::new(ConstructorExpression { type_name, fields })) | ||
| } | ||
| HirExpression::MemberAccess(access) => { | ||
| ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { | ||
| lhs: access.lhs.to_ast(interner), | ||
| rhs: access.rhs, | ||
| })) | ||
| } | ||
| HirExpression::Call(call) => { | ||
| let func = Box::new(call.func.to_ast(interner)); | ||
| let arguments = vecmap(call.arguments, |arg| arg.to_ast(interner)); | ||
| ExpressionKind::Call(Box::new(CallExpression { func, arguments })) | ||
| } | ||
| HirExpression::MethodCall(method_call) => { | ||
| ExpressionKind::MethodCall(Box::new(MethodCallExpression { | ||
| object: method_call.object.to_ast(interner), | ||
| method_name: method_call.method, | ||
| arguments: vecmap(method_call.arguments, |arg| arg.to_ast(interner)), | ||
| })) | ||
| } | ||
| HirExpression::Cast(cast) => { | ||
| let lhs = cast.lhs.to_ast(interner); | ||
| let r#type = cast.r#type.to_ast(); | ||
| ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type })) | ||
| } | ||
| HirExpression::If(if_expr) => ExpressionKind::If(Box::new(IfExpression { | ||
| condition: if_expr.condition.to_ast(interner), | ||
| consequence: if_expr.consequence.to_ast(interner), | ||
| alternative: if_expr.alternative.map(|expr| expr.to_ast(interner)), | ||
| })), | ||
| HirExpression::Tuple(fields) => { | ||
| ExpressionKind::Tuple(vecmap(fields, |field| field.to_ast(interner))) | ||
| } | ||
| HirExpression::Lambda(lambda) => { | ||
| let parameters = vecmap(lambda.parameters, |(pattern, typ)| { | ||
| (pattern.into_ast(interner), typ.to_ast()) | ||
| }); | ||
| let return_type = lambda.return_type.to_ast(); | ||
| let body = lambda.body.to_ast(interner); | ||
| ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) | ||
| } | ||
| HirExpression::Quote(block) => ExpressionKind::Quote(block), | ||
| HirExpression::Error => ExpressionKind::Error, | ||
| }; | ||
|
|
||
| Expression::new(kind, span) | ||
| } | ||
| } | ||
|
|
||
| impl HirPattern { | ||
| fn into_ast(self, interner: &NodeInterner) -> Pattern { | ||
| match self { | ||
| HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_ast(interner)), | ||
| HirPattern::Mutable(pattern, location) => { | ||
| let pattern = Box::new(pattern.into_ast(interner)); | ||
| Pattern::Mutable(pattern, location.span, false) | ||
| } | ||
| HirPattern::Tuple(patterns, location) => { | ||
| let patterns = vecmap(patterns, |pattern| pattern.into_ast(interner)); | ||
| Pattern::Tuple(patterns, location.span) | ||
| } | ||
| HirPattern::Struct(typ, patterns, location) => { | ||
| let patterns = | ||
| vecmap(patterns, |(name, pattern)| (name, pattern.into_ast(interner))); | ||
| let name = match typ.follow_bindings() { | ||
| Type::Struct(struct_def, _) => { | ||
| let struct_def = struct_def.borrow(); | ||
| struct_def.name.0.contents.clone() | ||
| } | ||
| // This pass shouldn't error so if the type isn't a struct we just get a string | ||
| // representation of any other type and use that. We're relying on name | ||
| // resolution to fail later when this Ast is re-converted to Hir. | ||
| other => other.to_string(), | ||
| }; | ||
| // The name span is lost here | ||
| let path = Path::from_single(name, location.span); | ||
| Pattern::Struct(path, patterns, location.span) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl HirIdent { | ||
| fn to_ast(&self, interner: &NodeInterner) -> Ident { | ||
| let name = interner.definition_name(self.id).to_owned(); | ||
| Ident(Spanned::from(self.location.span, name)) | ||
| } | ||
| } | ||
|
|
||
| impl Type { | ||
| fn to_ast(&self) -> UnresolvedType { | ||
| let typ = match self { | ||
| Type::FieldElement => UnresolvedTypeData::FieldElement, | ||
| Type::Array(length, element) => { | ||
| let length = length.to_type_expression(); | ||
| let element = Box::new(element.to_ast()); | ||
| UnresolvedTypeData::Array(length, element) | ||
| } | ||
| Type::Slice(element) => { | ||
| let element = Box::new(element.to_ast()); | ||
| UnresolvedTypeData::Slice(element) | ||
| } | ||
| Type::Integer(sign, bit_size) => UnresolvedTypeData::Integer(*sign, *bit_size), | ||
| Type::Bool => UnresolvedTypeData::Bool, | ||
| Type::String(length) => { | ||
| let length = length.to_type_expression(); | ||
| UnresolvedTypeData::String(Some(length)) | ||
| } | ||
| Type::FmtString(length, element) => { | ||
| let length = length.to_type_expression(); | ||
| let element = Box::new(element.to_ast()); | ||
| UnresolvedTypeData::FormatString(length, element) | ||
| } | ||
| Type::Unit => UnresolvedTypeData::Unit, | ||
| Type::Tuple(fields) => { | ||
| let fields = vecmap(fields, |field| field.to_ast()); | ||
| UnresolvedTypeData::Tuple(fields) | ||
| } | ||
| Type::Struct(def, generics) => { | ||
| let struct_def = def.borrow(); | ||
| let generics = vecmap(generics, |generic| generic.to_ast()); | ||
| let name = Path::from_ident(struct_def.name.clone()); | ||
| UnresolvedTypeData::Named(name, generics, false) | ||
| } | ||
| Type::Alias(type_def, generics) => { | ||
| // Keep the alias name instead of expanding this in case the | ||
| // alias' definition was changed | ||
| let type_def = type_def.borrow(); | ||
| let generics = vecmap(generics, |generic| generic.to_ast()); | ||
| let name = Path::from_ident(type_def.name.clone()); | ||
| UnresolvedTypeData::Named(name, generics, false) | ||
| } | ||
| Type::TypeVariable(_, _) => todo!("Convert Type::TypeVariable Hir -> Ast"), | ||
| Type::TraitAsType(_, name, generics) => { | ||
| let generics = vecmap(generics, |generic| generic.to_ast()); | ||
| let name = Path::from_single(name.as_ref().clone(), Span::default()); | ||
| UnresolvedTypeData::TraitAsType(name, generics) | ||
| } | ||
| Type::NamedGeneric(_, name) => { | ||
| let name = Path::from_single(name.as_ref().clone(), Span::default()); | ||
| UnresolvedTypeData::TraitAsType(name, Vec::new()) | ||
| } | ||
| Type::Function(args, ret, env) => { | ||
| let args = vecmap(args, |arg| arg.to_ast()); | ||
| let ret = Box::new(ret.to_ast()); | ||
| let env = Box::new(env.to_ast()); | ||
| UnresolvedTypeData::Function(args, ret, env) | ||
| } | ||
| Type::MutableReference(element) => { | ||
| let element = Box::new(element.to_ast()); | ||
| UnresolvedTypeData::MutableReference(element) | ||
| } | ||
| // Type::Forall is only for generic functions which don't store a type | ||
| // in their Ast so they don't need to call to_ast for their Forall type. | ||
| // Since there is no UnresolvedTypeData equivalent for Type::Forall, we use | ||
| // this to ignore this case since it shouldn't be needed anyway. | ||
| Type::Forall(_, typ) => return typ.to_ast(), | ||
| Type::Constant(_) => panic!("Type::Constant where a type was expected: {self:?}"), | ||
| Type::Code => UnresolvedTypeData::Code, | ||
| Type::Error => UnresolvedTypeData::Error, | ||
| }; | ||
|
|
||
| UnresolvedType { typ, span: None } | ||
| } | ||
|
|
||
| fn to_type_expression(&self) -> UnresolvedTypeExpression { | ||
| let span = Span::default(); | ||
michaeljklein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| match self.follow_bindings() { | ||
| Type::Constant(length) => UnresolvedTypeExpression::Constant(length, span), | ||
| Type::NamedGeneric(_, name) => { | ||
| let path = Path::from_single(name.as_ref().clone(), span); | ||
| UnresolvedTypeExpression::Variable(path) | ||
| } | ||
| // TODO: This should be turned into a proper error. | ||
| other => panic!("Cannot represent {other:?} as type expression"), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl HirLValue { | ||
| fn into_ast(self, interner: &NodeInterner) -> LValue { | ||
| match self { | ||
| HirLValue::Ident(ident, _) => LValue::Ident(ident.to_ast(interner)), | ||
| HirLValue::MemberAccess { object, field_name, field_index: _, typ: _, location } => { | ||
| let object = Box::new(object.into_ast(interner)); | ||
| LValue::MemberAccess { object, field_name, span: location.span } | ||
| } | ||
| HirLValue::Index { array, index, typ: _, location } => { | ||
| let array = Box::new(array.into_ast(interner)); | ||
| let index = index.to_ast(interner); | ||
| LValue::Index { array, index, span: location.span } | ||
| } | ||
| HirLValue::Dereference { lvalue, element_type: _, location } => { | ||
| let lvalue = Box::new(lvalue.into_ast(interner)); | ||
| LValue::Dereference(lvalue, location.span) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl HirArrayLiteral { | ||
| fn into_ast(self, interner: &NodeInterner, span: Span) -> ArrayLiteral { | ||
| match self { | ||
| HirArrayLiteral::Standard(elements) => { | ||
| ArrayLiteral::Standard(vecmap(elements, |element| element.to_ast(interner))) | ||
| } | ||
| HirArrayLiteral::Repeated { repeated_element, length } => { | ||
| let repeated_element = Box::new(repeated_element.to_ast(interner)); | ||
| let length = match length { | ||
| Type::Constant(length) => { | ||
| let literal = Literal::Integer((length as u128).into(), false); | ||
| let kind = ExpressionKind::Literal(literal); | ||
| Box::new(Expression::new(kind, span)) | ||
| } | ||
| other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), | ||
| }; | ||
| ArrayLiteral::Repeated { repeated_element, length } | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| mod hir_to_ast; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| pub mod comptime; | ||
| pub mod def_collector; | ||
| pub mod def_map; | ||
| pub mod resolution; | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.