diff --git a/vhdl_lang/src/analysis/declarative.rs b/vhdl_lang/src/analysis/declarative.rs index 45bedb8f..ca1c52a3 100644 --- a/vhdl_lang/src/analysis/declarative.rs +++ b/vhdl_lang/src/analysis/declarative.rs @@ -531,7 +531,9 @@ impl<'a> AnalyzeContext<'a> { } } } - + Declaration::SubprogramInstantiation(_) => { + // TODO: subprogram instantiation statement + } Declaration::Use(ref mut use_clause) => { self.analyze_use_clause(scope, use_clause, diagnostics)?; } diff --git a/vhdl_lang/src/analysis/named_entity.rs b/vhdl_lang/src/analysis/named_entity.rs index 45125045..c90374dd 100644 --- a/vhdl_lang/src/analysis/named_entity.rs +++ b/vhdl_lang/src/analysis/named_entity.rs @@ -11,7 +11,7 @@ use crate::ast::{ AttributeDeclaration, AttributeSpecification, ComponentDeclaration, Declaration, Designator, FileDeclaration, HasIdent, Ident, InterfaceFileDeclaration, InterfacePackageDeclaration, ObjectClass, ObjectDeclaration, PackageInstantiation, SubprogramBody, SubprogramDeclaration, - TypeDeclaration, WithDecl, + SubprogramInstantiation, TypeDeclaration, WithDecl, }; use crate::ast::{ExternalObjectClass, InterfaceDeclaration, InterfaceObjectDeclaration}; use crate::data::*; @@ -527,6 +527,7 @@ impl HasEntityId for Declaration { Declaration::Alias(alias) => alias.ent_id(), Declaration::SubprogramDeclaration(decl) => decl.ent_id(), Declaration::SubprogramBody(body) => body.ent_id(), + Declaration::SubprogramInstantiation(decl) => decl.ent_id(), Declaration::Package(pkg) => pkg.ent_id(), Declaration::Use(_) => None, Declaration::Configuration(_) => None, @@ -534,6 +535,12 @@ impl HasEntityId for Declaration { } } +impl HasEntityId for SubprogramInstantiation { + fn ent_id(&self) -> Option { + self.ident.decl + } +} + impl HasEntityId for PackageInstantiation { fn ent_id(&self) -> Option { self.ident.decl diff --git a/vhdl_lang/src/analysis/overloaded.rs b/vhdl_lang/src/analysis/overloaded.rs index 102d13e1..20ea6f7f 100644 --- a/vhdl_lang/src/analysis/overloaded.rs +++ b/vhdl_lang/src/analysis/overloaded.rs @@ -414,8 +414,8 @@ impl Diagnostic { #[cfg(test)] mod tests { - use super::*; + use crate::analysis::overloaded; use crate::analysis::tests::TestSetup; use crate::data::DiagnosticHandler; use crate::syntax::test::check_diagnostics; @@ -449,7 +449,7 @@ mod tests { &fcall.pos, &des, &mut fcall.item.parameters, - SubprogramKind::Function(ttyp), + overloaded::SubprogramKind::Function(ttyp), overloaded.entities().collect(), diagnostics, )) diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 64ffb30b..5fe471cd 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -476,6 +476,18 @@ impl> AsRef for WithDecl { } } +#[derive(PartialEq, Debug, Clone)] +pub struct WithToken { + item: T, + token: TokenId, +} + +impl WithToken { + pub fn new(item: T, token: TokenId) -> WithToken { + WithToken { item, token } + } +} + /// LRM 6.6 Alias declarations #[derive(PartialEq, Debug, Clone)] pub struct AliasDeclaration { @@ -686,6 +698,23 @@ pub struct SubprogramHeader { pub map_aspect: Option, } +#[derive(PartialEq, Debug, Clone)] +pub enum SubprogramKind { + Function, + Procedure, +} + +/// LRM 4.4 Subprogram Instantiation Statement +#[derive(PartialEq, Debug, Clone)] +pub struct SubprogramInstantiation { + pub kind: WithToken, + pub ident: WithDecl, + pub subprogram_name: WithPos, + pub signature: Option>, + pub generic_map: Option, + pub semi: TokenId, +} + /// LRM 4.5.3 Signatures #[derive(PartialEq, Debug, Clone)] pub enum Signature { @@ -780,6 +809,7 @@ pub enum Declaration { Attribute(Attribute), Alias(AliasDeclaration), SubprogramDeclaration(SubprogramDeclaration), + SubprogramInstantiation(SubprogramInstantiation), SubprogramBody(SubprogramBody), Use(UseClause), Package(PackageInstantiation), diff --git a/vhdl_lang/src/ast/display.rs b/vhdl_lang/src/ast/display.rs index 4a581300..7767591e 100644 --- a/vhdl_lang/src/ast/display.rs +++ b/vhdl_lang/src/ast/display.rs @@ -863,6 +863,43 @@ impl Display for SubprogramDeclaration { } } +impl Display for SubprogramInstantiation { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self.kind.item { + SubprogramKind::Function => write!(f, "function ")?, + SubprogramKind::Procedure => write!(f, "procedure ")?, + }; + write!(f, "{}", self.ident)?; + write!(f, " is new ")?; + write!(f, "{}", self.subprogram_name)?; + if let Some(signature) = &self.signature { + write!(f, " {}", signature)?; + } + if let Some(generic_map) = &self.generic_map { + writeln!(f, " generic map (")?; + write_separated_list(&generic_map.list, f, ",")?; + write!(f, "\n)")?; + } + Ok(()) + } +} + +fn write_separated_list( + list: &SeparatedList, + f: &mut Formatter<'_>, + separator: &str, +) -> Result { + let mut first = true; + for assoc in &list.items { + if !first { + writeln!(f, "{separator}")?; + } + write!(f, " {assoc}")?; + first = false; + } + Ok(()) +} + impl Display for InterfaceFileDeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "file {} : {}", self.ident, self.subtype_indication) @@ -2147,4 +2184,51 @@ end package;", }, ); } + + #[test] + fn write_subprogram_instantiation() { + assert_format_eq( + "function my_func is new func;", + "function my_func is new func", + Code::subprogram_instantiation, + ); + } + + #[test] + fn write_subprogram_instantiation_signature() { + assert_format_eq( + "function my_func is new func [bit return bit_vector];", + "function my_func is new func [bit return bit_vector]", + Code::subprogram_instantiation, + ); + } + + #[test] + fn write_subprogram_instantiation_signature_generic_map() { + assert_format_eq( + "procedure proc is new proc generic map (x => x, y => a.b);", + "procedure proc is new proc generic map ( + x => x, + y => a.b +)", + Code::subprogram_instantiation, + ); + } + + #[test] + fn subprogram_instantiation_declaration() { + assert_format_eq( + "procedure proc is new proc generic map (x => x, y => a.b);", + "procedure proc is new proc generic map ( + x => x, + y => a.b +)", + |code| { + assert_matches!( + code.declarative_part().remove(0), + Declaration::SubprogramInstantiation(inst) => inst + ) + }, + ); + } } diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index ba6854c4..64ae88d8 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -57,6 +57,7 @@ pub enum FoundDeclaration<'a> { Alias(&'a mut AliasDeclaration), Function(&'a mut FunctionSpecification), Procedure(&'a mut ProcedureSpecification), + SubprogramInstantiation(&'a mut SubprogramInstantiation), Package(&'a mut PackageDeclaration), PackageBody(&'a mut PackageBody), PackageInstance(&'a mut PackageInstantiation), @@ -1012,6 +1013,9 @@ impl Search for Declaration { Declaration::SubprogramDeclaration(decl) => { return_if_found!(decl.search(ctx, searcher)); } + Declaration::SubprogramInstantiation(decl) => { + return_if_found!(decl.search(ctx, searcher)); + } Declaration::Attribute(Attribute::Declaration(decl)) => { return_if_found!(searcher .search_decl(ctx, FoundDeclaration::Attribute(decl)) @@ -1358,6 +1362,19 @@ impl Search for MapAspect { } } +impl Search for SubprogramInstantiation { + fn search(&mut self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult { + return_if_found!(searcher + .search_decl(ctx, FoundDeclaration::SubprogramInstantiation(self)) + .or_not_found()); + return_if_found!(self.subprogram_name.search(ctx, searcher)); + if let Some(signature) = &mut self.signature { + return_if_found!(signature.item.search(ctx, searcher)); + } + self.generic_map.search(ctx, searcher) + } +} + // Search for reference to declaration/definition at cursor pub struct ItemAtCursor { source: Source, @@ -1670,6 +1687,7 @@ impl<'a> FoundDeclaration<'a> { FoundDeclaration::GenerateBody(..) => None, FoundDeclaration::ConcurrentStatement(..) => None, FoundDeclaration::SequentialStatement(..) => None, + FoundDeclaration::SubprogramInstantiation(_) => None, } } } @@ -1682,6 +1700,7 @@ impl<'a> HasEntityId for FoundDeclaration<'a> { FoundDeclaration::ForGenerateIndex(_, value) => value.index_name.decl, FoundDeclaration::Function(value) => value.designator.decl, FoundDeclaration::Procedure(value) => value.designator.decl, + FoundDeclaration::SubprogramInstantiation(value) => value.ident.decl, FoundDeclaration::Object(value) => value.ident.decl, FoundDeclaration::ElementDeclaration(elem) => elem.ident.decl, FoundDeclaration::EnumerationLiteral(_, elem) => elem.decl, @@ -1716,6 +1735,7 @@ impl<'a> HasSrcPos for FoundDeclaration<'a> { FoundDeclaration::ForIndex(ident, _) => ident.pos(), FoundDeclaration::ForGenerateIndex(_, value) => value.index_name.pos(), FoundDeclaration::Function(value) => &value.designator.tree.pos, + FoundDeclaration::SubprogramInstantiation(value) => &value.ident.tree.pos, FoundDeclaration::Procedure(value) => &value.designator.tree.pos, FoundDeclaration::Object(value) => value.ident.pos(), FoundDeclaration::ElementDeclaration(elem) => elem.ident.pos(), @@ -1762,6 +1782,9 @@ impl std::fmt::Display for FoundDeclaration<'_> { FoundDeclaration::Function(ref value) => { write!(f, "{value};") } + FoundDeclaration::SubprogramInstantiation(ref value) => { + write!(f, "{value};") + } FoundDeclaration::Procedure(ref value) => { write!(f, "{value};") } diff --git a/vhdl_lang/src/ast/visitor.rs b/vhdl_lang/src/ast/visitor.rs index 85d4acd7..68eff1f5 100644 --- a/vhdl_lang/src/ast/visitor.rs +++ b/vhdl_lang/src/ast/visitor.rs @@ -804,6 +804,14 @@ pub trait Visitor { fn visit_map_aspect(&mut self, _node: &MapAspect, _ctx: &dyn TokenAccess) -> VisitorResult { Continue } + + fn visit_subprogram_instantiation( + &mut self, + _node: &SubprogramInstantiation, + _ctx: &dyn TokenAccess, + ) -> VisitorResult { + Continue + } } /// An AST Node has two methods it needs to declare: @@ -1181,10 +1189,26 @@ impl ASTNode for Declaration { Declaration::Use(decl) => vec![decl], Declaration::Package(decl) => vec![decl], Declaration::Configuration(decl) => vec![decl], + Declaration::SubprogramInstantiation(decl) => vec![decl], } } } +impl ASTNode for SubprogramInstantiation { + fn visit(&self, visitor: &mut dyn Visitor, ctx: &dyn TokenAccess) -> VisitorResult { + visitor.visit_subprogram_instantiation(self, ctx) + } + + fn children(&self) -> Vec<&dyn ASTNode> { + vec![ + &self.ident, + &self.subprogram_name, + &self.signature, + &self.generic_map, + ] + } +} + impl ASTNode for ConfigurationSpecification { fn visit(&self, visitor: &mut dyn Visitor, ctx: &dyn TokenAccess) -> VisitorResult { visitor.visit_configuration_specification(self, ctx) diff --git a/vhdl_lang/src/syntax/subprogram.rs b/vhdl_lang/src/syntax/subprogram.rs index e01445f6..8fe29820 100644 --- a/vhdl_lang/src/syntax/subprogram.rs +++ b/vhdl_lang/src/syntax/subprogram.rs @@ -14,6 +14,7 @@ use crate::ast::*; use crate::data::*; use crate::syntax::concurrent_statement::parse_map_aspect; use crate::syntax::interface_declaration::parse_generic_interface_list; +use crate::syntax::names::parse_name; pub fn parse_signature(stream: &TokenStream) -> ParseResult> { let left_square = stream.expect_kind(LeftSquare)?; @@ -101,6 +102,44 @@ pub fn parse_optional_subprogram_header( })) } +pub fn parse_subprogram_instantiation( + stream: &TokenStream, + diagnostics: &mut dyn DiagnosticHandler, +) -> ParseResult { + let id = stream.get_token_id(); + let tok = stream.peek_expect()?; + let kind = match tok.kind { + Procedure => WithToken::new(SubprogramKind::Procedure, id), + Function => WithToken::new(SubprogramKind::Function, id), + _ => { + return Err(Diagnostic::error( + tok.pos.clone(), + "Expecting 'function' or 'procedure'", + )) + } + }; + stream.skip(); + let ident = WithDecl::new(stream.expect_ident()?); + stream.expect_kind(Is)?; + stream.expect_kind(New)?; + let subprogram_name = parse_name(stream)?; + let signature = if stream.next_kind_is(LeftSquare) { + Some(parse_signature(stream)?) + } else { + None + }; + let generic_map = parse_map_aspect(stream, Generic, diagnostics)?; + let semi = stream.expect_kind(SemiColon)?; + Ok(SubprogramInstantiation { + kind, + ident, + subprogram_name, + signature, + generic_map, + semi, + }) +} + pub fn parse_subprogram_declaration_no_semi( stream: &TokenStream, diagnostics: &mut dyn DiagnosticHandler, @@ -212,6 +251,13 @@ pub fn parse_subprogram( stream: &TokenStream, diagnostics: &mut dyn DiagnosticHandler, ) -> ParseResult { + if stream.next_kinds_are(&[Procedure, Identifier, Is, New]) + || stream.next_kinds_are(&[Function, Identifier, Is, New]) + { + return Ok(Declaration::SubprogramInstantiation( + parse_subprogram_instantiation(stream, diagnostics)?, + )); + } let specification = parse_subprogram_declaration_no_semi(stream, diagnostics)?; expect_token!( stream, @@ -782,4 +828,109 @@ end procedure swap; Declaration::SubprogramBody(body) ); } + + #[test] + pub fn subprogram_instantiation() { + let code = Code::new("procedure my_proc is new proc;"); + let inst = code.parse_ok_no_diagnostics(parse_subprogram_instantiation); + assert_eq!( + inst, + SubprogramInstantiation { + kind: WithToken::new(SubprogramKind::Procedure, code.s1("procedure").token()), + ident: code.s1("my_proc").decl_ident(), + subprogram_name: code.s1("new proc").s1("proc").name(), + signature: None, + generic_map: None, + semi: code.s1(";").token() + } + ); + + let code = Code::new("function my_func is new func;"); + let inst = code.parse_ok_no_diagnostics(parse_subprogram_instantiation); + assert_eq!( + inst, + SubprogramInstantiation { + kind: WithToken::new(SubprogramKind::Function, code.s1("function").token()), + ident: code.s1("my_func").decl_ident(), + subprogram_name: code.s1("new func").s1("func").name(), + signature: None, + generic_map: None, + semi: code.s1(";").token() + } + ); + + let code = Code::new("function my_func is new func [bit return bit_vector];"); + let inst = code.parse_ok_no_diagnostics(parse_subprogram_instantiation); + assert_eq!( + inst, + SubprogramInstantiation { + kind: WithToken::new(SubprogramKind::Function, code.s1("function").token()), + ident: code.s1("my_func").decl_ident(), + subprogram_name: code.s1("new func").s1("func").name(), + signature: Some(code.s1("[bit return bit_vector]").signature()), + generic_map: None, + semi: code.s1(";").token() + } + ); + + let code = + Code::new("function my_func is new func [bit return bit_vector] generic map (x => x);"); + let inst = code.parse_ok_no_diagnostics(parse_subprogram_instantiation); + assert_eq!( + inst, + SubprogramInstantiation { + kind: WithToken::new(SubprogramKind::Function, code.s1("function").token()), + ident: code.s1("my_func").decl_ident(), + subprogram_name: code.s1("new func").s1("func").name(), + signature: Some(code.s1("[bit return bit_vector]").signature()), + generic_map: Some(code.s1("generic map (x => x)").generic_map_aspect()), + semi: code.s1(";").token() + } + ); + + let code = Code::new("function my_func is new func generic map (z => z, x => y);"); + let inst = code.parse_ok_no_diagnostics(parse_subprogram_instantiation); + assert_eq!( + inst, + SubprogramInstantiation { + kind: WithToken::new(SubprogramKind::Function, code.s1("function").token()), + ident: code.s1("my_func").decl_ident(), + subprogram_name: code.s1("new func").s1("func").name(), + signature: None, + generic_map: Some(code.s1("generic map (z => z, x => y)").generic_map_aspect()), + semi: code.s1(";").token() + } + ); + } + + #[test] + pub fn subprogram_declaration() { + let code = Code::new("procedure my_proc is new proc;"); + let inst = code.parse_ok_no_diagnostics(parse_subprogram); + assert_eq!( + inst, + Declaration::SubprogramInstantiation(SubprogramInstantiation { + kind: WithToken::new(SubprogramKind::Procedure, code.s1("procedure").token()), + ident: code.s1("my_proc").decl_ident(), + subprogram_name: code.s1("new proc").s1("proc").name(), + signature: None, + generic_map: None, + semi: code.s1(";").token() + }) + ); + + let code = Code::new("function my_func is new func;"); + let inst = code.parse_ok_no_diagnostics(parse_subprogram); + assert_eq!( + inst, + Declaration::SubprogramInstantiation(SubprogramInstantiation { + kind: WithToken::new(SubprogramKind::Function, code.s1("function").token()), + ident: code.s1("my_func").decl_ident(), + subprogram_name: code.s1("new func").s1("func").name(), + signature: None, + generic_map: None, + semi: code.s1(";").token() + }) + ); + } } diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 7ed061ea..ce0001ab 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -32,7 +32,7 @@ use crate::data::*; use crate::syntax::concurrent_statement::parse_map_aspect; use crate::syntax::context::{parse_context, DeclarationOrReference}; use crate::syntax::names::parse_association_element; -use crate::syntax::subprogram::parse_optional_subprogram_header; +use crate::syntax::subprogram::{parse_optional_subprogram_header, parse_subprogram_instantiation}; use crate::syntax::{TokenAccess, TokenId}; use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::Entry; @@ -576,6 +576,10 @@ impl Code { self.parse_ok_no_diagnostics(parse_subprogram_declaration_no_semi) } + pub fn subprogram_instantiation(&self) -> SubprogramInstantiation { + self.parse_ok_no_diagnostics(parse_subprogram_instantiation) + } + pub fn subprogram_header(&self) -> Option { self.parse_ok_no_diagnostics(parse_optional_subprogram_header) }