use super::alias_declaration::parse_alias_declaration;
use super::attributes::parse_attribute;
use super::common::ParseResult;
use super::component_declaration::parse_component_declaration;
use super::configuration::parse_configuration_specification;
use super::context::parse_use_clause;
use super::names::parse_selected_name;
use super::object_declaration::{parse_file_declaration, parse_object_declaration};
use super::subprogram::parse_subprogram;
use super::tokens::{Kind::*, *};
use super::type_declaration::parse_type_declaration;
use crate::ast::token_range::WithTokenSpan;
use crate::ast::{ContextClause, Declaration, PackageInstantiation};
use crate::syntax::concurrent_statement::parse_map_aspect;
use crate::syntax::recover::expect_semicolon_or_last;
use crate::syntax::view::parse_mode_view_declaration;
use vhdl_lang::syntax::parser::ParsingContext;
pub fn parse_package_instantiation(
ctx: &mut ParsingContext<'_>,
) -> ParseResult<PackageInstantiation> {
let start_token = ctx.stream.expect_kind(Package)?;
let ident = ctx.stream.expect_ident()?;
ctx.stream.expect_kind(Is)?;
ctx.stream.expect_kind(New)?;
let package_name = parse_selected_name(ctx)?;
let generic_map = parse_map_aspect(ctx, Generic)?;
let end_token = expect_semicolon_or_last(ctx);
Ok(PackageInstantiation {
span: TokenSpan::new(start_token, end_token),
context_clause: ContextClause::default(),
ident: ident.into(),
package_name,
generic_map,
})
}
pub fn is_declarative_part(ctx: &mut ParsingContext<'_>) -> ParseResult<bool> {
Ok(matches!(
ctx.stream.peek_expect()?.kind,
Use | Type
| Subtype
| Shared
| Constant
| Signal
| Variable
| File
| Component
| Attribute
| Alias
| Impure
| Pure
| Function
| Procedure
| Package
| For
| View
| Begin
))
}
pub fn parse_declarative_part(
ctx: &mut ParsingContext<'_>,
) -> ParseResult<Vec<WithTokenSpan<Declaration>>> {
let mut declarations: Vec<WithTokenSpan<Declaration>> = Vec::new();
fn is_recover_token(kind: Kind) -> bool {
matches!(
kind,
Type | Subtype
| Component
| Impure
| Pure
| Function
| Procedure
| Package
| For
| File
| Shared
| Constant
| Signal
| Variable
| Attribute
| View
| Use
| Alias
| Begin
| End
)
}
while let Some(token) = ctx.stream.peek() {
let start_token = ctx.stream.get_current_token_id();
match token.kind {
Begin | End => break,
Type | Subtype | Component | Impure | Pure | Function | Procedure | Package | For => {
let decl = match token.kind {
Type | Subtype => parse_type_declaration(ctx).map(Declaration::Type)?,
Component => parse_component_declaration(ctx).map(Declaration::Component)?,
Impure | Pure | Function | Procedure => parse_subprogram(ctx)?,
Package => parse_package_instantiation(ctx).map(Declaration::Package)?,
For => {
parse_configuration_specification(ctx).map(Declaration::Configuration)?
}
_ => unreachable!(),
};
let end_token = ctx.stream.get_last_token_id();
declarations.push(WithTokenSpan::new(
decl,
TokenSpan::new(start_token, end_token),
));
}
File | Shared | Constant | Signal | Variable | Attribute => {
let decls: ParseResult<Vec<WithTokenSpan<Declaration>>> = match token.kind {
File => parse_file_declaration(ctx)
.map(|decl| vec![decl.map_into(Declaration::File)]),
Shared | Constant | Signal | Variable => parse_object_declaration(ctx)
.map(|decl| vec![decl.map_into(Declaration::Object)]),
Attribute => parse_attribute(ctx).map(|decls| {
decls
.into_iter()
.map(|decl| decl.map_into(Declaration::Attribute))
.collect()
}),
_ => unreachable!(),
};
match decls.or_recover_until(ctx, is_recover_token) {
Ok(ref mut decls) => declarations.append(decls),
Err(err) => {
ctx.diagnostics.push(err);
continue;
}
}
}
Use | Alias => {
let decl: ParseResult<WithTokenSpan<Declaration>> = match token.kind {
Use => parse_use_clause(ctx).map(|decl| decl.map_into(Declaration::Use)),
Alias => {
parse_alias_declaration(ctx).map(|decl| decl.map_into(Declaration::Alias))
}
_ => unreachable!(),
};
match decl.or_recover_until(ctx, is_recover_token) {
Ok(decl) => declarations.push(decl),
Err(err) => {
ctx.diagnostics.push(err);
continue;
}
}
}
View => {
match parse_mode_view_declaration(ctx).or_recover_until(ctx, is_recover_token) {
Ok(decl) => declarations.push(decl.map_into(Declaration::View)),
Err(err) => {
ctx.diagnostics.push(err);
continue;
}
}
}
_ => {
use crate::VHDLStandard::*;
let expected: &[Kind] = match ctx.standard {
VHDL2008 | VHDL1993 => &[
Type, Subtype, Component, Impure, Pure, Function, Procedure, Package, For,
File, Shared, Constant, Signal, Variable, Attribute, Use, Alias,
],
VHDL2019 => &[
Type, Subtype, Component, Impure, Pure, Function, Procedure, Package, For,
File, Shared, Constant, Signal, Variable, Attribute, Use, Alias, View,
],
};
ctx.diagnostics.push(token.kinds_error(expected));
ctx.stream.skip_until(is_recover_token)?;
continue;
}
}
}
Ok(declarations)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{ObjectClass, ObjectDeclaration};
use crate::data::Diagnostic;
use crate::syntax::test::{check_diagnostics, Code};
use crate::VHDLStandard::VHDL2019;
#[test]
fn package_instantiation() {
let code = Code::new(
"\
package ident is new lib.foo.bar;
",
);
assert_eq!(
code.with_stream_no_diagnostics(parse_package_instantiation),
PackageInstantiation {
span: code.token_span(),
context_clause: ContextClause::default(),
ident: code.s1("ident").decl_ident(),
package_name: code.s1("lib.foo.bar").name(),
generic_map: None
}
);
}
#[test]
fn package_instantiation_generic_map() {
let code = Code::new(
"\
package ident is new lib.foo.bar
generic map (
foo => bar
);
",
);
assert_eq!(
code.with_stream_no_diagnostics(parse_package_instantiation),
PackageInstantiation {
span: code.token_span(),
context_clause: ContextClause::default(),
ident: code.s1("ident").decl_ident(),
package_name: code.s1("lib.foo.bar").name(),
generic_map: Some(
code.s1("generic map (
foo => bar
)")
.generic_map_aspect()
)
}
);
}
#[test]
fn parse_declarative_part_recover() {
let code = Code::new(
"\
var invalid: broken;
constant x: natural := 5;
",
);
let (decls, msgs) = code.with_partial_stream_diagnostics(parse_declarative_part);
assert_eq!(
decls,
Ok(vec![WithTokenSpan::new(
Declaration::Object(ObjectDeclaration {
class: ObjectClass::Constant,
idents: vec![code.s1("x").decl_ident()],
colon_token: code.s(":", 2).token(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: Some(code.s1("5").expr())
}),
code.s1("constant x: natural := 5;").token_span()
)])
);
assert_eq!(
msgs,
vec![Diagnostic::syntax_error(
code.s1("var").pos(),
"Expected 'type', 'subtype', 'component', 'impure', 'pure', \
'function', 'procedure', 'package', 'for', 'file', \
'shared', 'constant', 'signal', 'variable', 'attribute', \
'use' or 'alias'"
)]
);
}
#[test]
fn parse_declarative_part_error() {
let code = Code::new("invalid");
let (decl, _) = code.with_partial_stream_diagnostics(parse_declarative_part);
assert!(decl.is_err());
}
#[test]
fn parse_declarative_part_vhdl2008_vs_vhdl2019() {
let code = Code::new(
"\
var not_a_var: broken;
",
);
let (_, diag) = code.with_partial_stream_diagnostics(parse_declarative_part);
check_diagnostics(
diag,
vec![Diagnostic::syntax_error(
code.s1("var").pos(),
"Expected 'type', 'subtype', 'component', 'impure', 'pure', \
'function', 'procedure', 'package', 'for', 'file', \
'shared', 'constant', 'signal', 'variable', 'attribute', \
'use' or 'alias'",
)],
);
let code = Code::with_standard(
"\
var not_a_var: broken;
",
VHDL2019,
);
let (_, diag) = code.with_partial_stream_diagnostics(parse_declarative_part);
check_diagnostics(
diag,
vec![Diagnostic::syntax_error(
code.s1("var").pos(),
"Expected 'type', 'subtype', 'component', 'impure', 'pure', \
'function', 'procedure', 'package', 'for', 'file', \
'shared', 'constant', 'signal', 'variable', 'attribute', \
'use', 'alias' or 'view'",
)],
)
}
}