mirror of
https://github.com/VHDL-LS/rust_hdl.git
synced 2025-12-23 06:01:10 +00:00
Add Formatting Capabilities (#321)
This PR enables the initial support for formatting VHDL language elements and files. The formatter is transforms the AST representation into a string. It is capable of formatting any AST element and preserving comments as well as newlines.
This commit is contained in:
parent
f5546fae78
commit
86eb38e8b3
78 changed files with 7746 additions and 993 deletions
1
clippy.toml
Normal file
1
clippy.toml
Normal file
|
|
@ -0,0 +1 @@
|
|||
ignore-interior-mutability = ["vhdl_lang::data::source::UniqueSource"]
|
||||
|
|
@ -38,7 +38,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.analyze_expression_for_target(scope, ttyp, item, diagnostics)?;
|
||||
self.boolean_expr(scope, condition, diagnostics)?;
|
||||
}
|
||||
if let Some(expr) = else_item {
|
||||
if let Some((expr, _)) = else_item {
|
||||
self.analyze_expression_for_target(scope, ttyp, expr, diagnostics)?;
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
alternatives,
|
||||
} = selection;
|
||||
let ctyp = as_fatal(self.expr_unambiguous_type(scope, expression, diagnostics))?;
|
||||
for Alternative { choices, item } in alternatives.iter_mut() {
|
||||
for Alternative {
|
||||
choices,
|
||||
item,
|
||||
span: _,
|
||||
} in alternatives.iter_mut()
|
||||
{
|
||||
self.analyze_expression_for_target(scope, ttyp, item, diagnostics)?;
|
||||
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
|
||||
}
|
||||
|
|
@ -83,7 +88,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.analyze_waveform(scope, ttyp, item, diagnostics)?;
|
||||
self.boolean_expr(scope, condition, diagnostics)?;
|
||||
}
|
||||
if let Some(wavf) = else_item {
|
||||
if let Some((wavf, _)) = else_item {
|
||||
self.analyze_waveform(scope, ttyp, wavf, diagnostics)?;
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +98,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
alternatives,
|
||||
} = selection;
|
||||
let ctyp = as_fatal(self.expr_unambiguous_type(scope, expression, diagnostics))?;
|
||||
for Alternative { choices, item } in alternatives.iter_mut() {
|
||||
for Alternative {
|
||||
choices,
|
||||
item,
|
||||
span: _,
|
||||
} in alternatives.iter_mut()
|
||||
{
|
||||
self.analyze_waveform(scope, ttyp, item, diagnostics)?;
|
||||
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
|
||||
}
|
||||
|
|
@ -119,7 +129,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Waveform::Unaffected => {}
|
||||
Waveform::Unaffected(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
// it must be a type conversion or a single parameter function call
|
||||
|
||||
let (pos, resolved_formal) = if let Some((inner_pos, inner_name)) =
|
||||
to_formal_conversion_argument(&mut fcall.parameters)
|
||||
to_formal_conversion_argument(&mut fcall.parameters.items)
|
||||
{
|
||||
(
|
||||
inner_pos,
|
||||
|
|
@ -250,7 +250,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
bail!(
|
||||
diagnostics,
|
||||
Diagnostic::new(
|
||||
&fcall.name.pos(self.ctx),
|
||||
fcall.name.pos(self.ctx),
|
||||
format!(
|
||||
"No function '{}' accepting {}",
|
||||
fcall.name,
|
||||
|
|
@ -372,7 +372,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
result.push((actual.span, Some(formal)));
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&actual.pos(self.ctx),
|
||||
actual.pos(self.ctx),
|
||||
"Unexpected extra argument",
|
||||
ErrorCode::TooManyArguments,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
..
|
||||
} = process;
|
||||
if let Some(sensitivity_list) = sensitivity_list {
|
||||
match sensitivity_list {
|
||||
match &mut sensitivity_list.item {
|
||||
SensitivityList::Names(names) => {
|
||||
self.sensitivity_list_check(scope, names, diagnostics)?;
|
||||
}
|
||||
|
|
@ -165,7 +165,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
let nested = scope.nested();
|
||||
self.analyze_generate_body(&nested, parent, item, src_span, diagnostics)?;
|
||||
}
|
||||
if let Some(ref mut else_item) = else_item {
|
||||
if let Some((ref mut else_item, _)) = else_item {
|
||||
let nested = scope.nested();
|
||||
self.analyze_generate_body(&nested, parent, else_item, src_span, diagnostics)?;
|
||||
}
|
||||
|
|
@ -186,6 +186,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
let Alternative {
|
||||
ref mut choices,
|
||||
ref mut item,
|
||||
span: _,
|
||||
} = alternative;
|
||||
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
|
||||
let nested = scope.nested();
|
||||
|
|
@ -258,7 +259,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
// Pre-declare labels
|
||||
self.define_labels_for_concurrent_part(scope, parent, statements, diagnostics)?;
|
||||
|
||||
if let Some(ref mut decl) = decl {
|
||||
if let Some((ref mut decl, _)) = decl {
|
||||
self.analyze_declarative_part(scope, parent, decl, diagnostics)?;
|
||||
}
|
||||
self.analyze_concurrent_part(scope, inner_parent, statements, diagnostics)?;
|
||||
|
|
@ -456,7 +457,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
))? {
|
||||
if object_name.base.class() != ObjectClass::Signal {
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
format!(
|
||||
"{} is not a signal and cannot be in a sensitivity list",
|
||||
object_name.base.describe_class()
|
||||
|
|
@ -467,7 +468,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
&& !object_name.base.is_port()
|
||||
{
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
format!(
|
||||
"{} cannot be in a sensitivity list",
|
||||
object_name.base.describe_class()
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
name,
|
||||
subtype_indication,
|
||||
signature,
|
||||
is_token: _,
|
||||
} = alias;
|
||||
|
||||
let resolved_name = self.name_resolve(scope, name.span, &mut name.item, diagnostics);
|
||||
|
|
@ -283,7 +284,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
));
|
||||
}
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
format!("{} cannot be aliased", resolved_name.describe_type()),
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -447,7 +448,8 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
Declaration::File(ref mut file) => {
|
||||
let FileDeclaration {
|
||||
ident,
|
||||
idents,
|
||||
colon_token: _,
|
||||
subtype_indication,
|
||||
open_info,
|
||||
file_name,
|
||||
|
|
@ -459,18 +461,20 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
diagnostics,
|
||||
))?;
|
||||
|
||||
if let Some(ref mut expr) = open_info {
|
||||
if let Some((_, ref mut expr)) = open_info {
|
||||
self.expr_unknown_ttyp(scope, expr, diagnostics)?;
|
||||
}
|
||||
if let Some(ref mut expr) = file_name {
|
||||
if let Some((_, ref mut expr)) = file_name {
|
||||
self.expr_unknown_ttyp(scope, expr, diagnostics)?;
|
||||
}
|
||||
|
||||
if let Some(subtype) = subtype {
|
||||
scope.add(
|
||||
self.define(ident, parent, AnyEntKind::File(subtype), src_span),
|
||||
diagnostics,
|
||||
);
|
||||
for ident in idents {
|
||||
scope.add(
|
||||
self.define(ident, parent, AnyEntKind::File(subtype), src_span),
|
||||
diagnostics,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Declaration::Component(ref mut component) => {
|
||||
|
|
@ -625,7 +629,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Type::Record(region) => region,
|
||||
_ => {
|
||||
let diag = Diagnostic::new(
|
||||
&view.typ.type_mark.pos(self.ctx),
|
||||
view.typ.type_mark.pos(self.ctx),
|
||||
format!(
|
||||
"The type of a view must be a record type, not {}",
|
||||
typ.type_mark().describe()
|
||||
|
|
@ -641,17 +645,17 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
};
|
||||
let mut unassociated: HashSet<_> = record_region.elems.iter().collect();
|
||||
for element in view.elements.iter_mut() {
|
||||
for name in element.names.items.iter_mut() {
|
||||
let desi = Designator::Identifier(name.item.item.clone());
|
||||
for name in element.names.iter_mut() {
|
||||
let desi = Designator::Identifier(name.tree.item.clone());
|
||||
let Some(record_element) = record_region.lookup(&desi) else {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
name.item.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
format!("Not a part of {}", typ.type_mark().describe()),
|
||||
ErrorCode::Unresolved,
|
||||
));
|
||||
continue;
|
||||
};
|
||||
name.set_unique_reference(&record_element);
|
||||
name.decl.set_unique_reference(&record_element);
|
||||
unassociated.remove(&record_element);
|
||||
}
|
||||
}
|
||||
|
|
@ -690,6 +694,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
entity_name,
|
||||
entity_class,
|
||||
expr,
|
||||
colon_token: _,
|
||||
} = attr_spec;
|
||||
|
||||
let attr_ent = match scope.lookup(
|
||||
|
|
@ -744,7 +749,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
if let Some(signature) = signature {
|
||||
diagnostics.push(Diagnostic::should_not_have_signature(
|
||||
"Attribute specification",
|
||||
&signature.pos(self.ctx),
|
||||
signature.pos(self.ctx),
|
||||
));
|
||||
}
|
||||
ent
|
||||
|
|
@ -954,7 +959,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
class,
|
||||
iface: Some(ObjectInterface::simple(
|
||||
object_decl.list_type,
|
||||
mode.mode.unwrap_or_default(),
|
||||
mode.mode.as_ref().map(|mode| mode.item).unwrap_or_default(),
|
||||
)),
|
||||
subtype,
|
||||
has_default: mode.expression.is_some(),
|
||||
|
|
@ -968,14 +973,14 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
let resolved =
|
||||
self.name_resolve(scope, view.name.span, &mut view.name.item, diagnostics)?;
|
||||
let view_ent = self.resolve_view_ent(&resolved, diagnostics, view.name.span)?;
|
||||
if let Some(ast_declared_subtype) = &mut view.subtype_indication {
|
||||
if let Some((_, ast_declared_subtype)) = &mut view.subtype_indication {
|
||||
let declared_subtype =
|
||||
self.resolve_subtype_indication(scope, ast_declared_subtype, diagnostics)?;
|
||||
if declared_subtype.type_mark() != view_ent.subtype().type_mark() {
|
||||
bail!(
|
||||
diagnostics,
|
||||
Diagnostic::new(
|
||||
&ast_declared_subtype.type_mark.pos(self.ctx),
|
||||
ast_declared_subtype.type_mark.pos(self.ctx),
|
||||
"Specified subtype must match the subtype declared for the view",
|
||||
ErrorCode::TypeMismatch
|
||||
)
|
||||
|
|
@ -1061,7 +1066,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ArrayIndex::IndexSubtypeDefintion(ref mut type_mark) => self
|
||||
.type_name(scope, type_mark.span, &mut type_mark.item, diagnostics)
|
||||
.map(|typ| typ.base()),
|
||||
ArrayIndex::Discrete(ref mut drange) => self.drange_type(scope, drange, diagnostics),
|
||||
ArrayIndex::Discrete(ref mut drange) => {
|
||||
self.drange_type(scope, &mut drange.item, diagnostics)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ResolvedName::Library(ref library_name) => {
|
||||
if library_name != self.work_library_name() {
|
||||
diagnostics.add(
|
||||
&prefix.pos(self.ctx),
|
||||
prefix.pos(self.ctx),
|
||||
format!("Configuration must be within the same library '{}' as the corresponding entity", self.work_library_name()), ErrorCode::ConfigNotInSameLibrary);
|
||||
Err(EvalError::Unknown)
|
||||
} else {
|
||||
|
|
@ -451,14 +451,14 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
}
|
||||
other => {
|
||||
diagnostics.push(other.kind_error(&prefix.pos(self.ctx), "library"));
|
||||
diagnostics.push(other.kind_error(prefix.pos(self.ctx), "library"));
|
||||
Err(EvalError::Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
diagnostics.add(
|
||||
&ent_name.pos(self.ctx),
|
||||
ent_name.pos(self.ctx),
|
||||
"Expected selected name",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -480,7 +480,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
bail!(
|
||||
diagnostics,
|
||||
Diagnostic::mismatched_kinds(
|
||||
&prefix.pos(self.ctx),
|
||||
prefix.pos(self.ctx),
|
||||
"Invalid prefix of a selected name",
|
||||
)
|
||||
);
|
||||
|
|
@ -490,7 +490,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
bail!(
|
||||
diagnostics,
|
||||
Diagnostic::mismatched_kinds(
|
||||
&prefix.pos(self.ctx),
|
||||
prefix.pos(self.ctx),
|
||||
"'.all' may not be the prefix of a selected name",
|
||||
)
|
||||
);
|
||||
|
|
@ -531,7 +531,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
| Name::External(..) => {
|
||||
bail!(
|
||||
diagnostics,
|
||||
Diagnostic::mismatched_kinds(&name.pos(self.ctx), "Invalid selected name",)
|
||||
Diagnostic::mismatched_kinds(name.pos(self.ctx), "Invalid selected name",)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -548,7 +548,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ContextItem::Library(LibraryClause {
|
||||
ref mut name_list, ..
|
||||
}) => {
|
||||
for library_name in name_list.items.iter_mut() {
|
||||
for library_name in name_list.iter_mut() {
|
||||
if self.work_sym == library_name.item.item {
|
||||
library_name.set_unique_reference(self.work_library());
|
||||
diagnostics.add(
|
||||
|
|
@ -577,12 +577,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ContextItem::Context(ContextReference {
|
||||
ref mut name_list, ..
|
||||
}) => {
|
||||
for name in name_list.items.iter_mut() {
|
||||
for name in name_list.iter_mut() {
|
||||
match name.item {
|
||||
Name::Selected(..) => {}
|
||||
_ => {
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
"Context reference must be a selected name",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -639,13 +639,13 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
use_clause: &mut UseClause,
|
||||
diagnostics: &mut dyn DiagnosticHandler,
|
||||
) -> FatalResult {
|
||||
for name in use_clause.name_list.items.iter_mut() {
|
||||
for name in use_clause.name_list.iter_mut() {
|
||||
match name.item {
|
||||
Name::Selected(..) => {}
|
||||
Name::SelectedAll(..) => {}
|
||||
_ => {
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
"Use clause must be a selected name",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -729,7 +729,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
}
|
||||
diagnostics.add(
|
||||
&package_name.pos(self.ctx),
|
||||
package_name.pos(self.ctx),
|
||||
format!("'{package_name}' is not an uninstantiated generic package"),
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -487,6 +487,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
.resolve_subtype_indication(scope, subtype, diagnostics)
|
||||
.map(|typ| ExpressionType::Unambiguous(typ.type_mark())),
|
||||
},
|
||||
Expression::Parenthesized(expr) => {
|
||||
self.expr_pos_type(scope, expr.span, &mut expr.item, diagnostics)
|
||||
}
|
||||
Expression::Literal(ref mut literal) => match literal {
|
||||
Literal::Physical(PhysicalLiteral { ref mut unit, .. }) => {
|
||||
match self.resolve_physical_unit(scope, unit) {
|
||||
|
|
@ -678,7 +681,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
let mut diag = Diagnostic::new(
|
||||
&expr.pos(self.ctx),
|
||||
expr.pos(self.ctx),
|
||||
"Ambiguous use of implicit boolean conversion ??",
|
||||
ErrorCode::AmbiguousCall,
|
||||
);
|
||||
|
|
@ -842,7 +845,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
target_base,
|
||||
indexes,
|
||||
*elem_type,
|
||||
assoc,
|
||||
&mut assoc.item,
|
||||
diagnostics,
|
||||
))?;
|
||||
}
|
||||
|
|
@ -870,6 +873,15 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Expression::New(ref mut alloc) => {
|
||||
self.analyze_allocation(scope, alloc, diagnostics)?;
|
||||
}
|
||||
Expression::Parenthesized(ref mut expr) => {
|
||||
self.expr_pos_with_ttyp(
|
||||
scope,
|
||||
target_type,
|
||||
expr.span,
|
||||
&mut expr.item,
|
||||
diagnostics,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -878,11 +890,11 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
pub fn analyze_aggregate(
|
||||
&self,
|
||||
scope: &Scope<'a>,
|
||||
assocs: &mut [ElementAssociation],
|
||||
assocs: &mut [WithTokenSpan<ElementAssociation>],
|
||||
diagnostics: &mut dyn DiagnosticHandler,
|
||||
) -> FatalResult {
|
||||
for assoc in assocs.iter_mut() {
|
||||
match assoc {
|
||||
match &mut assoc.item {
|
||||
ElementAssociation::Named(ref mut choices, ref mut expr) => {
|
||||
for choice in choices.iter_mut() {
|
||||
match choice.item {
|
||||
|
|
@ -911,14 +923,14 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
record_type: TypeEnt<'a>,
|
||||
elems: &RecordRegion<'a>,
|
||||
span: TokenSpan,
|
||||
assocs: &mut [ElementAssociation],
|
||||
assocs: &mut [WithTokenSpan<ElementAssociation>],
|
||||
diagnostics: &mut dyn DiagnosticHandler,
|
||||
) -> FatalResult {
|
||||
let mut associated = RecordAssociations::default();
|
||||
let mut is_ok_so_far = true;
|
||||
|
||||
for (idx, assoc) in assocs.iter_mut().enumerate() {
|
||||
match assoc {
|
||||
match &mut assoc.item {
|
||||
ElementAssociation::Named(ref mut choices, ref mut actual_expr) => {
|
||||
let typ = if choices.len() == 1 {
|
||||
let choice = choices.first_mut().unwrap();
|
||||
|
|
@ -949,7 +961,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
} else {
|
||||
is_ok_so_far = false;
|
||||
diagnostics.add(
|
||||
&choice.pos(self.ctx),
|
||||
choice.pos(self.ctx),
|
||||
"Record aggregate choice must be a simple name",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -959,7 +971,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Choice::DiscreteRange(_) => {
|
||||
is_ok_so_far = false;
|
||||
diagnostics.add(
|
||||
&choice.pos(self.ctx),
|
||||
choice.pos(self.ctx),
|
||||
"Record aggregate choice must be a simple name",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -979,7 +991,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
.collect();
|
||||
|
||||
if remaining_types.len() > 1 {
|
||||
let mut diag = Diagnostic::new(&choice.pos(self.ctx), format!("Other elements of record '{}' are not of the same type", record_type.designator()), ErrorCode::TypeMismatch);
|
||||
let mut diag = Diagnostic::new(choice.pos(self.ctx), format!("Other elements of record '{}' are not of the same type", record_type.designator()), ErrorCode::TypeMismatch);
|
||||
for elem in elems.iter() {
|
||||
if !associated.is_associated(&elem) {
|
||||
if let Some(decl_pos) = elem.decl_pos() {
|
||||
|
|
@ -998,7 +1010,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
} else if remaining_types.is_empty() {
|
||||
diagnostics.push(
|
||||
Diagnostic::new(
|
||||
&choice.pos(self.ctx),
|
||||
choice.pos(self.ctx),
|
||||
format!(
|
||||
"All elements of record '{}' are already associated",
|
||||
record_type.designator()
|
||||
|
|
@ -1190,13 +1202,13 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
array_type,
|
||||
&index_types[1..],
|
||||
elem_type,
|
||||
assoc,
|
||||
&mut assoc.item,
|
||||
diagnostics,
|
||||
))?;
|
||||
}
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&expr.pos(self.ctx),
|
||||
expr.pos(self.ctx),
|
||||
format!(
|
||||
"Expected sub-aggregate for target {}",
|
||||
array_type.describe()
|
||||
|
|
@ -1280,7 +1292,7 @@ mod test {
|
|||
use crate::analysis::tests::TestSetup;
|
||||
use crate::data::DiagnosticHandler;
|
||||
use crate::syntax::test::check_diagnostics;
|
||||
use crate::syntax::test::without_releated;
|
||||
use crate::syntax::test::without_related;
|
||||
use crate::syntax::test::Code;
|
||||
|
||||
impl<'a> TestSetup<'a> {
|
||||
|
|
@ -1438,7 +1450,7 @@ mod test {
|
|||
assert_eq!(test.expr_type(&code, &mut diagnostics), None);
|
||||
|
||||
check_diagnostics(
|
||||
without_releated(&diagnostics),
|
||||
without_related(&diagnostics),
|
||||
vec![Diagnostic::new(
|
||||
code.s1("and"),
|
||||
"Found no match for operator \"and\"",
|
||||
|
|
@ -1476,7 +1488,7 @@ mod test {
|
|||
assert_eq!(test.expr_type(&code, &mut diagnostics), None);
|
||||
|
||||
check_diagnostics(
|
||||
without_releated(&diagnostics),
|
||||
without_related(&diagnostics),
|
||||
vec![Diagnostic::new(
|
||||
code.s1("missing"),
|
||||
"No declaration of 'missing'",
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@ impl<'a> SplitName<'a> {
|
|||
),
|
||||
Name::CallOrIndexed(ref mut fcall) => SplitName::Suffix(
|
||||
&mut fcall.name,
|
||||
Suffix::CallOrIndexed(&mut fcall.parameters),
|
||||
Suffix::CallOrIndexed(&mut fcall.parameters.items),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -789,7 +789,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
idx as usize
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&expr.span.pos(self.ctx),
|
||||
expr.span.pos(self.ctx),
|
||||
"Expected an integer literal",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -810,7 +810,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
if let Some(expr) = expr {
|
||||
let ndims = indexes.len();
|
||||
let dimensions = plural("dimension", "dimensions", ndims);
|
||||
diagnostics.add(&expr.pos(self.ctx), format!("Index {idx} out of range for array with {ndims} {dimensions}, expected 1 to {ndims}"), ErrorCode::DimensionMismatch);
|
||||
diagnostics.add(expr.pos(self.ctx), format!("Index {idx} out of range for array with {ndims} {dimensions}, expected 1 to {ndims}"), ErrorCode::DimensionMismatch);
|
||||
}
|
||||
Err(EvalError::Unknown)
|
||||
}
|
||||
|
|
@ -1278,7 +1278,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ResolvedName::Expression(DisambiguatedType::Ambiguous(types));
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&prefix.pos(self.ctx),
|
||||
prefix.pos(self.ctx),
|
||||
"Procedure calls are not valid in names and expressions",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -1293,7 +1293,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ResolvedName::Expression(DisambiguatedType::Unambiguous(typ));
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&prefix.pos(self.ctx),
|
||||
prefix.pos(self.ctx),
|
||||
"Procedure calls are not valid in names and expressions",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -1356,7 +1356,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ResolvedName::Expression(DisambiguatedType::Ambiguous(types));
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&prefix.pos(self.ctx),
|
||||
prefix.pos(self.ctx),
|
||||
"Procedure calls are not valid in names and expressions",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -1371,7 +1371,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
);
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&prefix.pos(self.ctx),
|
||||
prefix.pos(self.ctx),
|
||||
"Procedure calls are not valid in names and expressions",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -1959,7 +1959,7 @@ fn check_no_attr_argument(
|
|||
) {
|
||||
if let Some(ref expr) = suffix.expr {
|
||||
diagnostics.add(
|
||||
&expr.pos(ctx),
|
||||
expr.pos(ctx),
|
||||
format!("'{} attribute does not take an argument", suffix.attr),
|
||||
ErrorCode::TooManyArguments,
|
||||
)
|
||||
|
|
@ -2259,7 +2259,7 @@ variable c0 : integer_vector(0 to 1);
|
|||
check_diagnostics(
|
||||
diagnostics,
|
||||
vec![Diagnostic::new(
|
||||
&code.s1("c0"),
|
||||
code.s1("c0"),
|
||||
"variable 'c0' cannot be called as a function",
|
||||
ErrorCode::InvalidCall,
|
||||
)],
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ mod tests {
|
|||
&self.scope,
|
||||
&fcall.pos(&code.tokenize()),
|
||||
&des,
|
||||
&mut fcall.item.parameters,
|
||||
&mut fcall.item.parameters.items,
|
||||
overloaded::SubprogramKind::Function(ttyp),
|
||||
overloaded.entities().collect(),
|
||||
diagnostics,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&formal.pos(self.ctx),
|
||||
formal.pos(self.ctx),
|
||||
"Expected simple name for package generic formal",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -86,7 +86,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ent
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
"Extra actual for generic map",
|
||||
ErrorCode::TooManyArguments,
|
||||
);
|
||||
|
|
@ -118,7 +118,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
format!(
|
||||
"Array constraint cannot be used for {}",
|
||||
typ.describe()
|
||||
|
|
@ -141,7 +141,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
"Cannot map expression to type generic",
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
@ -172,7 +172,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
name.set_unique_reference(&ent);
|
||||
} else {
|
||||
let mut diag = Diagnostic::mismatched_kinds(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
format!(
|
||||
"Cannot map '{des}' to subprogram generic {}{}",
|
||||
target.designator(),
|
||||
|
|
@ -189,7 +189,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
format!(
|
||||
"Cannot map {} to subprogram generic",
|
||||
resolved.describe()
|
||||
|
|
@ -201,14 +201,14 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Expression::Literal(Literal::String(string)) => {
|
||||
if Operator::from_latin1(string.clone()).is_none() {
|
||||
diagnostics.add(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
"Invalid operator symbol",
|
||||
ErrorCode::InvalidOperatorSymbol,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => diagnostics.add(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
"Cannot map expression to subprogram generic",
|
||||
ErrorCode::MismatchedKinds,
|
||||
),
|
||||
|
|
@ -218,7 +218,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.name_resolve(scope, assoc.actual.span, name, diagnostics)?;
|
||||
}
|
||||
_ => diagnostics.add(
|
||||
&assoc.actual.pos(self.ctx),
|
||||
assoc.actual.pos(self.ctx),
|
||||
"Cannot map expression to package generic",
|
||||
ErrorCode::MismatchedKinds,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Ok(DisambiguatedType::Unambiguous(typ))
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&expr.pos(self.ctx),
|
||||
expr.pos(self.ctx),
|
||||
format!("Non-scalar {} cannot be used in a range", typ.describe()),
|
||||
ErrorCode::NonScalarInRange,
|
||||
);
|
||||
|
|
@ -63,7 +63,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
)),
|
||||
ExpressionType::String | ExpressionType::Null | ExpressionType::Aggregate => {
|
||||
diagnostics.add(
|
||||
&expr.pos(self.ctx),
|
||||
expr.pos(self.ctx),
|
||||
"Non-scalar expression cannot be used in a range",
|
||||
ErrorCode::NonScalarInRange,
|
||||
);
|
||||
|
|
@ -94,7 +94,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
ent.return_type().unwrap()
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&attr.name.pos(self.ctx),
|
||||
attr.name.pos(self.ctx),
|
||||
format!(
|
||||
"{} cannot be prefix of range attribute, array type or object is required",
|
||||
resolved.describe()
|
||||
|
|
@ -113,7 +113,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
| ResolvedName::Library(_)
|
||||
| ResolvedName::Design(_) => {
|
||||
diagnostics.add(
|
||||
&attr.name.pos(self.ctx),
|
||||
attr.name.pos(self.ctx),
|
||||
format!(
|
||||
"{} cannot be prefix of range attribute, array type or object is required",
|
||||
resolved.describe()
|
||||
|
|
@ -144,7 +144,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&attr.name.pos(self.ctx),
|
||||
attr.name.pos(self.ctx),
|
||||
format!(
|
||||
"{} cannot be prefix of range attribute, array type or object is required",
|
||||
resolved.describe()
|
||||
|
|
@ -270,7 +270,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Ok(typ)
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&drange.span().pos(self.ctx),
|
||||
drange.span().pos(self.ctx),
|
||||
format!(
|
||||
"Non-discrete {} cannot be used in discrete range",
|
||||
typ.describe()
|
||||
|
|
@ -327,7 +327,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
|
||||
if let Some(ref mut signature) = signature {
|
||||
diagnostics.add(
|
||||
&signature.pos(self.ctx),
|
||||
signature.pos(self.ctx),
|
||||
format!("Did not expect signature for '{attr} attribute"),
|
||||
ErrorCode::UnexpectedSignature,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1291,16 +1291,15 @@ impl<'a> EntHierarchy<'a> {
|
|||
|
||||
fn public_symbols<'a>(ent: EntRef<'a>) -> Box<dyn Iterator<Item = EntRef<'a>> + 'a> {
|
||||
match ent.kind() {
|
||||
AnyEntKind::Design(d) => match d {
|
||||
AnyEntKind::Design(
|
||||
Design::Entity(_, region)
|
||||
| Design::Package(_, region)
|
||||
| Design::UninstPackage(_, region) => Box::new(
|
||||
region
|
||||
.immediates()
|
||||
.flat_map(|ent| std::iter::once(ent).chain(public_symbols(ent))),
|
||||
),
|
||||
_ => Box::new(std::iter::empty()),
|
||||
},
|
||||
| Design::UninstPackage(_, region),
|
||||
) => Box::new(
|
||||
region
|
||||
.immediates()
|
||||
.flat_map(|ent| std::iter::once(ent).chain(public_symbols(ent))),
|
||||
),
|
||||
AnyEntKind::Type(t) => match t {
|
||||
Type::Protected(region, is_body) if !is_body => Box::new(region.immediates()),
|
||||
_ => Box::new(std::iter::empty()),
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Some(resolved) => resolved,
|
||||
None => {
|
||||
// Continue checking missing names even if procedure is not found
|
||||
self.analyze_assoc_elems(scope, parameters, diagnostics)?;
|
||||
self.analyze_assoc_elems(scope, &mut parameters.items, diagnostics)?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
|
@ -86,7 +86,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
scope,
|
||||
&fcall_span.pos(self.ctx),
|
||||
des,
|
||||
parameters,
|
||||
&mut parameters.items,
|
||||
SubprogramKind::Procedure,
|
||||
names.entities().collect(),
|
||||
diagnostics,
|
||||
|
|
@ -99,7 +99,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
|
||||
if !ent.is_procedure() {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
"Invalid procedure call",
|
||||
ErrorCode::InvalidCall,
|
||||
);
|
||||
|
|
@ -114,7 +114,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
diagnostics.push(diagnostic);
|
||||
} else if ent.is_uninst_subprogram_body() {
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
format!("uninstantiated {} cannot be called", ent.describe()),
|
||||
ErrorCode::InvalidCall,
|
||||
)
|
||||
|
|
@ -143,20 +143,20 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
)?;
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
format!("{} is not a procedure", resolved.describe_type()),
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
self.analyze_assoc_elems(scope, parameters, diagnostics)?;
|
||||
self.analyze_assoc_elems(scope, &mut parameters.items, diagnostics)?;
|
||||
}
|
||||
}
|
||||
resolved => {
|
||||
diagnostics.add(
|
||||
&name.pos(self.ctx),
|
||||
name.pos(self.ctx),
|
||||
format!("{} is not a procedure", resolved.describe_type()),
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
self.analyze_assoc_elems(scope, parameters, diagnostics)?;
|
||||
self.analyze_assoc_elems(scope, &mut parameters.items, diagnostics)?;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
diagnostics,
|
||||
)?;
|
||||
}
|
||||
if let Some(else_item) = else_item {
|
||||
if let Some((else_item, _)) = else_item {
|
||||
self.define_labels_for_sequential_part(
|
||||
scope,
|
||||
parent,
|
||||
|
|
@ -122,7 +122,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.expr_with_ttyp(scope, ttyp, expression, diagnostics)?;
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&statement.statement.pos(self.ctx),
|
||||
statement.statement.pos(self.ctx),
|
||||
"Functions cannot return without a value",
|
||||
ErrorCode::VoidReturn,
|
||||
);
|
||||
|
|
@ -131,7 +131,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
SequentialRoot::Procedure => {
|
||||
if expression.is_some() {
|
||||
diagnostics.add(
|
||||
&statement.statement.pos(self.ctx),
|
||||
statement.statement.pos(self.ctx),
|
||||
"Procedures cannot return a value",
|
||||
ErrorCode::NonVoidReturn,
|
||||
);
|
||||
|
|
@ -139,7 +139,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
SequentialRoot::Process => {
|
||||
diagnostics.add(
|
||||
&statement.statement.pos(self.ctx),
|
||||
statement.statement.pos(self.ctx),
|
||||
"Cannot return from a process",
|
||||
ErrorCode::IllegalReturn,
|
||||
);
|
||||
|
|
@ -152,7 +152,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
condition_clause,
|
||||
timeout_clause,
|
||||
} = wait_stmt;
|
||||
self.sensitivity_list_check(scope, sensitivity_clause, diagnostics)?;
|
||||
if let Some(list) = sensitivity_clause {
|
||||
self.sensitivity_list_check(scope, list, diagnostics)?;
|
||||
}
|
||||
if let Some(expr) = condition_clause {
|
||||
self.boolean_expr(scope, expr, diagnostics)?;
|
||||
}
|
||||
|
|
@ -191,7 +193,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.check_loop_label(scope, parent, loop_label, diagnostics);
|
||||
} else if !find_outer_loop(parent, None) {
|
||||
diagnostics.add(
|
||||
&statement_span.pos(self.ctx),
|
||||
statement_span.pos(self.ctx),
|
||||
"Exit can only be used inside a loop",
|
||||
ErrorCode::ExitOutsideLoop,
|
||||
)
|
||||
|
|
@ -211,7 +213,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.check_loop_label(scope, parent, loop_label, diagnostics);
|
||||
} else if !find_outer_loop(parent, None) {
|
||||
diagnostics.add(
|
||||
&statement_span.pos(self.ctx),
|
||||
statement_span.pos(self.ctx),
|
||||
"Next can only be used inside a loop",
|
||||
ErrorCode::NextOutsideLoop,
|
||||
)
|
||||
|
|
@ -233,20 +235,23 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.boolean_expr(scope, condition, diagnostics)?;
|
||||
self.analyze_sequential_part(scope, parent, item, diagnostics)?;
|
||||
}
|
||||
if let Some(else_item) = else_item {
|
||||
if let Some((else_item, _)) = else_item {
|
||||
self.analyze_sequential_part(scope, parent, else_item, diagnostics)?;
|
||||
}
|
||||
}
|
||||
SequentialStatement::Case(ref mut case_stmt) => {
|
||||
let CaseStatement {
|
||||
is_matching: _,
|
||||
expression,
|
||||
alternatives,
|
||||
end_label_pos: _,
|
||||
..
|
||||
} = case_stmt;
|
||||
let ctyp = as_fatal(self.expr_unambiguous_type(scope, expression, diagnostics))?;
|
||||
for alternative in alternatives.iter_mut() {
|
||||
let Alternative { choices, item } = alternative;
|
||||
let Alternative {
|
||||
choices,
|
||||
item,
|
||||
span: _,
|
||||
} = alternative;
|
||||
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
|
||||
self.analyze_sequential_part(scope, parent, item, diagnostics)?;
|
||||
}
|
||||
|
|
@ -255,7 +260,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
let LoopStatement {
|
||||
iteration_scheme,
|
||||
statements,
|
||||
end_label_pos: _,
|
||||
..
|
||||
} = loop_stmt;
|
||||
match iteration_scheme {
|
||||
Some(IterationScheme::For(ref mut index, ref mut drange)) => {
|
||||
|
|
|
|||
|
|
@ -428,7 +428,6 @@ mod test_mod {
|
|||
"XXXX01LH",
|
||||
),
|
||||
(BitString::new(None, BaseSpecifier::UO, "27"), "010111"),
|
||||
// (BitString::new(None, BaseSpecifier::UO, "2C"), "011CCC"), // TODO: is this an error in the spec?
|
||||
(BitString::new(None, BaseSpecifier::SX, "3W"), "0011WWWW"),
|
||||
(BitString::new(None, BaseSpecifier::D, "35"), "100011"),
|
||||
(
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
resolved_ent
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&instantiation.subprogram_name.pos(self.ctx),
|
||||
instantiation.subprogram_name.pos(self.ctx),
|
||||
format!(
|
||||
"No uninstantiated subprogram exists with signature {}",
|
||||
key.describe()
|
||||
|
|
@ -373,7 +373,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
// There are multiple candidates
|
||||
// and there is no signature to resolve
|
||||
let mut err = Diagnostic::new(
|
||||
&instantiation.subprogram_name.pos(self.ctx),
|
||||
instantiation.subprogram_name.pos(self.ctx),
|
||||
format!("Ambiguous instantiation of '{}'", overloaded.designator()),
|
||||
ErrorCode::AmbiguousInstantiation,
|
||||
);
|
||||
|
|
@ -388,7 +388,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
_ => {
|
||||
diagnostics.add(
|
||||
&instantiation.subprogram_name.pos(self.ctx),
|
||||
instantiation.subprogram_name.pos(self.ctx),
|
||||
format!(
|
||||
"{} does not denote an uninstantiated subprogram",
|
||||
name.describe()
|
||||
|
|
@ -402,7 +402,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
Ok(overloaded_ent)
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&instantiation.subprogram_name.pos(self.ctx),
|
||||
instantiation.subprogram_name.pos(self.ctx),
|
||||
format!("{} cannot be instantiated", overloaded_ent.describe()),
|
||||
ErrorCode::MismatchedKinds,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ end package body;
|
|||
check_diagnostics(
|
||||
diagnostics,
|
||||
vec![Diagnostic::new(
|
||||
&code.s1("a1"),
|
||||
code.s1("a1"),
|
||||
"Deferred constants are only allowed in package declarations (not body)",
|
||||
ErrorCode::IllegalDeferredConstant,
|
||||
)],
|
||||
|
|
@ -88,11 +88,11 @@ end package;
|
|||
check_diagnostics(
|
||||
diagnostics,
|
||||
vec![Diagnostic::new(
|
||||
&code.s("a1", 1),
|
||||
code.s("a1", 1),
|
||||
"Deferred constant 'a1' lacks corresponding full constant declaration in package body",
|
||||
ErrorCode::MissingDeferredDeclaration
|
||||
),Diagnostic::new(
|
||||
&code.s("a1", 2),
|
||||
code.s("a1", 2),
|
||||
"Full declaration of deferred constant is only allowed in a package body",
|
||||
ErrorCode::IllegalDeferredConstant
|
||||
)],
|
||||
|
|
@ -123,12 +123,12 @@ end package body;
|
|||
diagnostics,
|
||||
vec![
|
||||
Diagnostic::new(
|
||||
&code.s1("a1"),
|
||||
code.s1("a1"),
|
||||
"Deferred constant 'a1' lacks corresponding full constant declaration in package body",
|
||||
ErrorCode::MissingDeferredDeclaration
|
||||
),
|
||||
Diagnostic::new(
|
||||
&code.s1("b1"),
|
||||
code.s1("b1"),
|
||||
"Deferred constant 'b1' lacks corresponding full constant declaration in package body",
|
||||
ErrorCode::MissingDeferredDeclaration
|
||||
),
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ end package;
|
|||
|
||||
let diagnostics = builder.analyze();
|
||||
check_diagnostics(
|
||||
without_releated(&diagnostics),
|
||||
without_related(&diagnostics),
|
||||
vec![Diagnostic::new(
|
||||
code.sa("bad_to_string is ", "to_string"),
|
||||
"Could not find declaration of 'to_string' with given signature",
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ fn check_analysis_equal(got: &mut DesignRoot, expected: &mut DesignRoot) -> Vec<
|
|||
check_diagnostics(got_diagnostics.clone(), expected_diagnostics);
|
||||
|
||||
// Check that all references are equal, ensures the incremental
|
||||
// analysis has cleared refereces
|
||||
// analysis has cleared references
|
||||
let mut got_searcher = FindAnyReferences::default();
|
||||
let _ = got.search(&mut got_searcher);
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,11 @@ impl MockTokenAccess {
|
|||
}
|
||||
|
||||
impl TokenAccess for MockTokenAccess {
|
||||
fn get_token(&self, _id: TokenId) -> &Token {
|
||||
fn get_token(&self, _id: TokenId) -> Option<&Token> {
|
||||
Some(&self.token)
|
||||
}
|
||||
|
||||
fn index(&self, _id: TokenId) -> &Token {
|
||||
&self.token
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ end package body;
|
|||
diagnostics,
|
||||
vec![
|
||||
Diagnostic::new(
|
||||
&code.s1("a1"),
|
||||
code.s1("a1"),
|
||||
"Missing body for protected type 'a1'",
|
||||
ErrorCode::MissingProtectedBodyType,
|
||||
),
|
||||
Diagnostic::new(
|
||||
&code.s1("b1"),
|
||||
code.s1("b1"),
|
||||
"Missing body for protected type 'b1'",
|
||||
ErrorCode::MissingProtectedBodyType,
|
||||
),
|
||||
|
|
@ -75,17 +75,17 @@ end package body;
|
|||
diagnostics,
|
||||
vec![
|
||||
Diagnostic::new(
|
||||
&code.s1("a1"),
|
||||
code.s1("a1"),
|
||||
"No declaration of protected type 'a1'",
|
||||
ErrorCode::Unresolved,
|
||||
),
|
||||
Diagnostic::new(
|
||||
&code.s1("b1"),
|
||||
code.s1("b1"),
|
||||
"No declaration of protected type 'b1'",
|
||||
ErrorCode::Unresolved,
|
||||
),
|
||||
Diagnostic::new(
|
||||
&code.s("b1", 2),
|
||||
code.s("b1", 2),
|
||||
"Missing body for protected type 'b1'",
|
||||
ErrorCode::MissingProtectedBodyType,
|
||||
),
|
||||
|
|
@ -224,7 +224,7 @@ end package body;
|
|||
let expected = vec![
|
||||
duplicate(&code, "a1", 1, 2),
|
||||
Diagnostic::new(
|
||||
&code.s("b1", 2),
|
||||
code.s("b1", 2),
|
||||
"'b1' is not a protected type",
|
||||
ErrorCode::TypeMismatch,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -265,13 +265,10 @@ end procedure;
|
|||
let (root, diagnostics) = builder.get_analyzed_root();
|
||||
check_no_diagnostics(&diagnostics);
|
||||
|
||||
if false {
|
||||
// @TODO uncomment when analyzing for loop type
|
||||
assert_eq!(
|
||||
root.search_reference_pos(code.source(), code.s1("theproc(i)").start()),
|
||||
Some(code.s1("theproc").pos())
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
root.search_reference_pos(code.source(), code.s1("theproc(i)").start()),
|
||||
Some(code.s1("theproc").pos())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -694,7 +694,7 @@ signal bad2 : natural := string'(\"hello\");
|
|||
diagnostics,
|
||||
vec![
|
||||
Diagnostic::new(
|
||||
code.s1("(\"hello\")"),
|
||||
code.s1("\"hello\""),
|
||||
"string literal does not match integer type 'INTEGER'",
|
||||
ErrorCode::TypeMismatch,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
}
|
||||
}
|
||||
}
|
||||
TypeDefinition::Array(ref mut array_indexes, ref mut subtype_indication) => {
|
||||
TypeDefinition::Array(ref mut array_indexes, _, ref mut subtype_indication) => {
|
||||
let mut indexes: Vec<Option<BaseType>> = Vec::with_capacity(array_indexes.len());
|
||||
for index in array_indexes.iter_mut() {
|
||||
indexes.push(as_fatal(self.analyze_array_index(
|
||||
|
|
@ -399,11 +399,11 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
scope.add(primary, diagnostics);
|
||||
|
||||
for (secondary_unit_name, value) in physical.secondary_units.iter_mut() {
|
||||
match self.resolve_physical_unit(scope, &mut value.unit) {
|
||||
match self.resolve_physical_unit(scope, &mut value.item.unit) {
|
||||
Ok(secondary_unit_type) => {
|
||||
if secondary_unit_type.base_type() != phys_type {
|
||||
diagnostics.add(
|
||||
value.unit.item.pos(self.ctx),
|
||||
value.item.unit.item.pos(self.ctx),
|
||||
format!(
|
||||
"Physical unit of type '{}' does not match {}",
|
||||
secondary_unit_type.designator(),
|
||||
|
|
@ -451,7 +451,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
UniversalType::Real
|
||||
} else {
|
||||
diagnostics.add(
|
||||
&range.span().pos(self.ctx),
|
||||
range.span().pos(self.ctx),
|
||||
"Expected real or integer range",
|
||||
ErrorCode::TypeMismatch,
|
||||
);
|
||||
|
|
@ -558,11 +558,11 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
|
|||
self.drange_with_ttyp(
|
||||
scope,
|
||||
(*index_typ).into(),
|
||||
drange,
|
||||
&mut drange.item,
|
||||
diagnostics,
|
||||
)?;
|
||||
} else {
|
||||
self.drange_unknown_type(scope, drange, diagnostics)?;
|
||||
self.drange_unknown_type(scope, &mut drange.item, diagnostics)?;
|
||||
}
|
||||
} else {
|
||||
diagnostics.add(
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ pub mod token_range;
|
|||
|
||||
pub(crate) use self::util::*;
|
||||
use crate::ast::token_range::*;
|
||||
pub(crate) use any_design_unit::*;
|
||||
|
||||
use crate::data::*;
|
||||
use crate::named_entity::{EntityId, Reference};
|
||||
use crate::syntax::{Token, TokenAccess, TokenId};
|
||||
pub(crate) use any_design_unit::*;
|
||||
use vhdl_lang::HasTokenSpan;
|
||||
|
||||
/// LRM 15.8 Bit string literals
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
|
|
@ -178,6 +178,7 @@ pub enum ExternalPath {
|
|||
pub struct ExternalName {
|
||||
pub class: ExternalObjectClass,
|
||||
pub path: WithTokenSpan<ExternalPath>,
|
||||
pub colon_token: TokenId,
|
||||
pub subtype: SubtypeIndication,
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +198,7 @@ pub enum Name {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct CallOrIndexed {
|
||||
pub name: WithTokenSpan<Name>,
|
||||
pub parameters: Vec<AssociationElement>,
|
||||
pub parameters: SeparatedList<AssociationElement>,
|
||||
}
|
||||
|
||||
/// LRM 9.3.3 Aggregates
|
||||
|
|
@ -286,7 +287,7 @@ pub enum Expression {
|
|||
Unary(WithToken<WithRef<Operator>>, Box<WithTokenSpan<Expression>>),
|
||||
|
||||
/// LRM 9.3.3 Aggregates
|
||||
Aggregate(Vec<ElementAssociation>),
|
||||
Aggregate(Vec<WithTokenSpan<ElementAssociation>>),
|
||||
|
||||
/// LRM 9.3.5 Qualified expressions
|
||||
Qualified(Box<QualifiedExpression>),
|
||||
|
|
@ -299,6 +300,7 @@ pub enum Expression {
|
|||
|
||||
/// LRM 9.3.7 Allocators
|
||||
New(Box<WithTokenSpan<Allocator>>),
|
||||
Parenthesized(Box<WithTokenSpan<Expression>>),
|
||||
}
|
||||
|
||||
/// An identifier together with the lexical source location it occurs in.
|
||||
|
|
@ -328,6 +330,12 @@ pub struct RangeConstraint {
|
|||
pub right_expr: Box<WithTokenSpan<Expression>>,
|
||||
}
|
||||
|
||||
impl RangeConstraint {
|
||||
pub fn direction_token(&self) -> TokenId {
|
||||
self.left_expr.span.end_token + 1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Range {
|
||||
Range(RangeConstraint),
|
||||
|
|
@ -346,7 +354,7 @@ pub enum SubtypeConstraint {
|
|||
Range(Range),
|
||||
/// Empty Vec means Open
|
||||
Array(
|
||||
Vec<DiscreteRange>,
|
||||
Vec<WithTokenSpan<DiscreteRange>>,
|
||||
Option<Box<WithTokenSpan<SubtypeConstraint>>>,
|
||||
),
|
||||
Record(Vec<ElementConstraint>),
|
||||
|
|
@ -364,27 +372,44 @@ pub struct RecordElementResolution {
|
|||
pub enum ResolutionIndication {
|
||||
FunctionName(WithTokenSpan<Name>),
|
||||
ArrayElement(WithTokenSpan<Name>),
|
||||
Record(Vec<RecordElementResolution>),
|
||||
Unresolved,
|
||||
Record(WithTokenSpan<Vec<RecordElementResolution>>),
|
||||
}
|
||||
|
||||
impl HasTokenSpan for ResolutionIndication {
|
||||
fn get_start_token(&self) -> TokenId {
|
||||
match self {
|
||||
ResolutionIndication::FunctionName(name) => name.get_start_token(),
|
||||
ResolutionIndication::ArrayElement(name) => name.get_start_token() - 1,
|
||||
ResolutionIndication::Record(record) => record.get_start_token(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_end_token(&self) -> TokenId {
|
||||
match self {
|
||||
ResolutionIndication::FunctionName(name) => name.get_end_token(),
|
||||
ResolutionIndication::ArrayElement(name) => name.get_end_token() + 1,
|
||||
ResolutionIndication::Record(record) => record.get_end_token(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// LRM 6.3 Subtype declarations
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct SubtypeIndication {
|
||||
pub resolution: ResolutionIndication,
|
||||
pub resolution: Option<ResolutionIndication>,
|
||||
pub type_mark: WithTokenSpan<Name>,
|
||||
pub constraint: Option<WithTokenSpan<SubtypeConstraint>>,
|
||||
}
|
||||
|
||||
/// LRM 5.3 Array Types
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
#[derive(PartialEq, Debug, Clone, TokenSpan)]
|
||||
pub enum ArrayIndex {
|
||||
/// Unbounded
|
||||
/// {identifier} range <>
|
||||
IndexSubtypeDefintion(WithTokenSpan<Name>),
|
||||
|
||||
/// Constraint
|
||||
Discrete(DiscreteRange),
|
||||
Discrete(WithTokenSpan<DiscreteRange>),
|
||||
}
|
||||
|
||||
/// LRM 5.3.3 Record types
|
||||
|
|
@ -392,6 +417,7 @@ pub enum ArrayIndex {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ElementDeclaration {
|
||||
pub idents: Vec<WithDecl<Ident>>,
|
||||
pub colon_token: TokenId,
|
||||
pub subtype: SubtypeIndication,
|
||||
}
|
||||
|
||||
|
|
@ -470,6 +496,7 @@ impl HasDesignator for WithToken<WithRef<Designator>> {
|
|||
pub struct AliasDeclaration {
|
||||
pub designator: WithDecl<WithToken<Designator>>,
|
||||
pub subtype_indication: Option<SubtypeIndication>,
|
||||
pub is_token: TokenId,
|
||||
pub name: WithTokenSpan<Name>,
|
||||
pub signature: Option<WithTokenSpan<Signature>>,
|
||||
}
|
||||
|
|
@ -526,6 +553,7 @@ pub enum EntityClass {
|
|||
pub struct AttributeSpecification {
|
||||
pub ident: WithRef<Ident>,
|
||||
pub entity_name: EntityName,
|
||||
pub colon_token: TokenId,
|
||||
pub entity_class: EntityClass,
|
||||
pub expr: WithTokenSpan<Expression>,
|
||||
}
|
||||
|
|
@ -553,8 +581,9 @@ pub struct ProtectedTypeBody {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct PhysicalTypeDeclaration {
|
||||
pub range: Range,
|
||||
pub units_token: TokenId,
|
||||
pub primary_unit: WithDecl<Ident>,
|
||||
pub secondary_units: Vec<(WithDecl<Ident>, PhysicalLiteral)>,
|
||||
pub secondary_units: Vec<(WithDecl<Ident>, WithTokenSpan<PhysicalLiteral>)>,
|
||||
}
|
||||
|
||||
/// LRM 5.2.2 Enumeration types
|
||||
|
|
@ -578,7 +607,7 @@ pub enum TypeDefinition {
|
|||
// @TODO floating
|
||||
/// LRM 5.3 Composite Types
|
||||
/// LRM 5.3.2 Array types
|
||||
Array(Vec<ArrayIndex>, SubtypeIndication),
|
||||
Array(Vec<ArrayIndex>, TokenId, SubtypeIndication),
|
||||
/// LRM 5.3.3 Record types
|
||||
Record(Vec<ElementDeclaration>),
|
||||
/// LRM 5.4 Access types
|
||||
|
|
@ -603,6 +632,17 @@ pub struct TypeDeclaration {
|
|||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
impl TypeDeclaration {
|
||||
pub fn is_token(&self) -> Option<TokenId> {
|
||||
if matches!(self.def, TypeDefinition::Incomplete(_)) {
|
||||
// incomplete types have no `is` token
|
||||
None
|
||||
} else {
|
||||
Some(self.ident.tree.token + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// LRM 6.4.2 Object Declarations
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum ObjectClass {
|
||||
|
|
@ -622,6 +662,7 @@ pub enum InterfaceType {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ObjectDeclaration {
|
||||
pub class: ObjectClass,
|
||||
pub colon_token: TokenId,
|
||||
pub idents: Vec<WithDecl<Ident>>,
|
||||
pub subtype_indication: SubtypeIndication,
|
||||
pub expression: Option<WithTokenSpan<Expression>>,
|
||||
|
|
@ -629,10 +670,11 @@ pub struct ObjectDeclaration {
|
|||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct FileDeclaration {
|
||||
pub ident: WithDecl<Ident>,
|
||||
pub idents: Vec<WithDecl<Ident>>,
|
||||
pub colon_token: TokenId,
|
||||
pub subtype_indication: SubtypeIndication,
|
||||
pub open_info: Option<WithTokenSpan<Expression>>,
|
||||
pub file_name: Option<WithTokenSpan<Expression>>,
|
||||
pub open_info: Option<(TokenId, WithTokenSpan<Expression>)>,
|
||||
pub file_name: Option<(TokenId, WithTokenSpan<Expression>)>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
|
|
@ -674,7 +716,9 @@ pub struct FunctionSpecification {
|
|||
pub struct SubprogramBody {
|
||||
pub specification: SubprogramSpecification,
|
||||
pub declarations: Vec<WithTokenSpan<Declaration>>,
|
||||
pub begin_token: TokenId,
|
||||
pub statements: Vec<LabeledSequentialStatement>,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
|
|
@ -729,6 +773,7 @@ pub struct SubprogramDeclaration {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct InterfaceFileDeclaration {
|
||||
pub idents: Vec<WithDecl<Ident>>,
|
||||
pub colon_token: TokenId,
|
||||
pub subtype_indication: SubtypeIndication,
|
||||
}
|
||||
|
||||
|
|
@ -737,6 +782,7 @@ pub struct InterfaceFileDeclaration {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct InterfaceObjectDeclaration {
|
||||
pub list_type: InterfaceType,
|
||||
pub colon_token: TokenId,
|
||||
pub idents: Vec<WithDecl<Ident>>,
|
||||
pub mode: ModeIndication,
|
||||
}
|
||||
|
|
@ -749,7 +795,7 @@ pub enum ModeIndication {
|
|||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct SimpleModeIndication {
|
||||
pub mode: Option<Mode>,
|
||||
pub mode: Option<WithToken<Mode>>,
|
||||
pub class: ObjectClass,
|
||||
pub subtype_indication: SubtypeIndication,
|
||||
pub bus: bool,
|
||||
|
|
@ -762,11 +808,12 @@ pub enum ModeViewIndicationKind {
|
|||
Record,
|
||||
}
|
||||
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ModeViewIndication {
|
||||
pub kind: ModeViewIndicationKind,
|
||||
pub name: WithTokenSpan<Name>,
|
||||
pub subtype_indication: Option<SubtypeIndication>,
|
||||
pub subtype_indication: Option<(TokenId, SubtypeIndication)>,
|
||||
}
|
||||
|
||||
/// LRM 6.5.5 Interface package declaration
|
||||
|
|
@ -783,7 +830,7 @@ pub enum InterfacePackageGenericMapAspect {
|
|||
pub struct InterfacePackageDeclaration {
|
||||
pub ident: WithDecl<Ident>,
|
||||
pub package_name: WithTokenSpan<Name>,
|
||||
pub generic_map: InterfacePackageGenericMapAspect,
|
||||
pub generic_map: WithTokenSpan<InterfacePackageGenericMapAspect>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
|
|
@ -825,8 +872,10 @@ pub enum Mode {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ComponentDeclaration {
|
||||
pub ident: WithDecl<Ident>,
|
||||
pub is_token: Option<TokenId>,
|
||||
pub generic_list: Option<InterfaceList>,
|
||||
pub port_list: Option<InterfaceList>,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
|
|
@ -849,14 +898,37 @@ pub enum Declaration {
|
|||
|
||||
impl Declaration {
|
||||
pub fn declarations(&self) -> Vec<EntityId> {
|
||||
todo!()
|
||||
match self {
|
||||
Declaration::Object(ObjectDeclaration { idents, .. })
|
||||
| Declaration::File(FileDeclaration { idents, .. }) => {
|
||||
idents.iter().flat_map(|ident| ident.decl.get()).collect()
|
||||
}
|
||||
Declaration::Type(TypeDeclaration { ident, .. })
|
||||
| Declaration::Component(ComponentDeclaration { ident, .. })
|
||||
| Declaration::View(ModeViewDeclaration { ident, .. })
|
||||
| Declaration::Package(PackageInstantiation { ident, .. })
|
||||
| Declaration::SubprogramInstantiation(SubprogramInstantiation { ident, .. })
|
||||
| Declaration::Attribute(Attribute::Declaration(AttributeDeclaration {
|
||||
ident, ..
|
||||
})) => ident.decl.get().into_iter().collect(),
|
||||
Declaration::Alias(alias) => alias.designator.decl.get().into_iter().collect(),
|
||||
Declaration::SubprogramDeclaration(SubprogramDeclaration { specification, .. })
|
||||
| Declaration::SubprogramBody(SubprogramBody { specification, .. }) => {
|
||||
let designator = match specification {
|
||||
SubprogramSpecification::Procedure(procedure) => &procedure.designator,
|
||||
SubprogramSpecification::Function(function) => &function.designator,
|
||||
};
|
||||
designator.decl.get().into_iter().collect()
|
||||
}
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// LRM 10.2 Wait statement
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct WaitStatement {
|
||||
pub sensitivity_clause: Vec<WithTokenSpan<Name>>,
|
||||
pub sensitivity_clause: Option<Vec<WithTokenSpan<Name>>>,
|
||||
pub condition_clause: Option<WithTokenSpan<Expression>>,
|
||||
pub timeout_clause: Option<WithTokenSpan<Expression>>,
|
||||
}
|
||||
|
|
@ -880,7 +952,7 @@ pub struct ReportStatement {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Target {
|
||||
Name(Name),
|
||||
Aggregate(Vec<ElementAssociation>),
|
||||
Aggregate(Vec<WithTokenSpan<ElementAssociation>>),
|
||||
}
|
||||
|
||||
/// LRM 10.5 Signal assignment statement
|
||||
|
|
@ -890,11 +962,24 @@ pub struct WaveformElement {
|
|||
pub after: Option<WithTokenSpan<Expression>>,
|
||||
}
|
||||
|
||||
impl HasTokenSpan for WaveformElement {
|
||||
fn get_start_token(&self) -> TokenId {
|
||||
self.value.get_start_token()
|
||||
}
|
||||
|
||||
fn get_end_token(&self) -> TokenId {
|
||||
self.after
|
||||
.as_ref()
|
||||
.map(|expr| expr.get_end_token())
|
||||
.unwrap_or(self.value.get_end_token())
|
||||
}
|
||||
}
|
||||
|
||||
/// LRM 10.5 Signal assignment statement
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Waveform {
|
||||
Elements(Vec<WaveformElement>),
|
||||
Unaffected,
|
||||
Unaffected(TokenId),
|
||||
}
|
||||
|
||||
/// LRM 10.5 Signal assignment statement
|
||||
|
|
@ -910,7 +995,7 @@ pub enum DelayMechanism {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct SignalAssignment {
|
||||
pub target: WithTokenSpan<Target>,
|
||||
pub delay_mechanism: Option<DelayMechanism>,
|
||||
pub delay_mechanism: Option<WithTokenSpan<DelayMechanism>>,
|
||||
pub rhs: AssignmentRightHand<Waveform>,
|
||||
}
|
||||
|
||||
|
|
@ -959,7 +1044,7 @@ pub struct Conditional<T> {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct Conditionals<T> {
|
||||
pub conditionals: Vec<Conditional<T>>,
|
||||
pub else_item: Option<T>,
|
||||
pub else_item: Option<(T, TokenId)>,
|
||||
}
|
||||
|
||||
/// LRM 10.8 If statement
|
||||
|
|
@ -969,6 +1054,7 @@ pub struct IfStatement {
|
|||
pub end_label_pos: Option<SrcPos>,
|
||||
}
|
||||
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct Alternative<T> {
|
||||
pub choices: Vec<WithTokenSpan<Choice>>,
|
||||
|
|
@ -987,6 +1073,7 @@ pub struct CaseStatement {
|
|||
pub is_matching: bool,
|
||||
pub expression: WithTokenSpan<Expression>,
|
||||
pub alternatives: Vec<Alternative<Vec<LabeledSequentialStatement>>>,
|
||||
pub end_token: TokenId,
|
||||
pub end_label_pos: Option<SrcPos>,
|
||||
}
|
||||
|
||||
|
|
@ -1001,7 +1088,9 @@ pub enum IterationScheme {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct LoopStatement {
|
||||
pub iteration_scheme: Option<IterationScheme>,
|
||||
pub loop_token: TokenId,
|
||||
pub statements: Vec<LabeledSequentialStatement>,
|
||||
pub end_token: TokenId,
|
||||
pub end_label_pos: Option<SrcPos>,
|
||||
}
|
||||
|
||||
|
|
@ -1058,8 +1147,11 @@ pub struct LabeledSequentialStatement {
|
|||
pub struct BlockStatement {
|
||||
pub guard_condition: Option<WithTokenSpan<Expression>>,
|
||||
pub header: BlockHeader,
|
||||
pub is_token: Option<TokenId>,
|
||||
pub decl: Vec<WithTokenSpan<Declaration>>,
|
||||
pub begin_token: TokenId,
|
||||
pub statements: Vec<LabeledConcurrentStatement>,
|
||||
pub end_token: TokenId,
|
||||
pub end_label_pos: Option<SrcPos>,
|
||||
}
|
||||
|
||||
|
|
@ -1083,9 +1175,12 @@ pub enum SensitivityList {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ProcessStatement {
|
||||
pub postponed: bool,
|
||||
pub sensitivity_list: Option<SensitivityList>,
|
||||
pub sensitivity_list: Option<WithTokenSpan<SensitivityList>>,
|
||||
pub is_token: Option<TokenId>,
|
||||
pub decl: Vec<WithTokenSpan<Declaration>>,
|
||||
pub begin_token: TokenId,
|
||||
pub statements: Vec<LabeledSequentialStatement>,
|
||||
pub end_token: TokenId,
|
||||
pub end_label_pos: Option<SrcPos>,
|
||||
}
|
||||
|
||||
|
|
@ -1130,12 +1225,10 @@ impl InstantiatedUnit {
|
|||
}
|
||||
}
|
||||
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct MapAspect {
|
||||
// `generic` or `map`
|
||||
pub start: TokenId,
|
||||
pub list: SeparatedList<AssociationElement>,
|
||||
pub closing_paren: TokenId,
|
||||
}
|
||||
|
||||
impl MapAspect {
|
||||
|
|
@ -1143,11 +1236,6 @@ impl MapAspect {
|
|||
pub fn formals(&self) -> impl Iterator<Item = Option<EntityId>> + '_ {
|
||||
self.list.formals()
|
||||
}
|
||||
|
||||
/// Returns the span that this aspect encompasses
|
||||
pub fn span(&self, ctx: &dyn TokenAccess) -> SrcPos {
|
||||
ctx.get_span(self.start, self.closing_paren)
|
||||
}
|
||||
}
|
||||
|
||||
/// 11.7 Component instantiation statements
|
||||
|
|
@ -1170,9 +1258,10 @@ impl InstantiationStatement {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct GenerateBody {
|
||||
pub alternative_label: Option<WithDecl<Ident>>,
|
||||
pub decl: Option<Vec<WithTokenSpan<Declaration>>>,
|
||||
pub decl: Option<(Vec<WithTokenSpan<Declaration>>, TokenId)>,
|
||||
pub statements: Vec<LabeledConcurrentStatement>,
|
||||
pub end_label_pos: Option<SrcPos>,
|
||||
pub end_token: Option<TokenId>,
|
||||
pub end_label: Option<TokenId>,
|
||||
}
|
||||
|
||||
/// 11.8 Generate statements
|
||||
|
|
@ -1181,7 +1270,9 @@ pub struct GenerateBody {
|
|||
pub struct ForGenerateStatement {
|
||||
pub index_name: WithDecl<Ident>,
|
||||
pub discrete_range: DiscreteRange,
|
||||
pub generate_token: TokenId,
|
||||
pub body: GenerateBody,
|
||||
pub end_token: TokenId,
|
||||
pub end_label_pos: Option<SrcPos>,
|
||||
}
|
||||
|
||||
|
|
@ -1197,6 +1288,7 @@ pub struct IfGenerateStatement {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct CaseGenerateStatement {
|
||||
pub sels: Selection<GenerateBody>,
|
||||
pub end_token: TokenId,
|
||||
pub end_label_pos: Option<SrcPos>,
|
||||
}
|
||||
|
||||
|
|
@ -1205,14 +1297,17 @@ pub struct CaseGenerateStatement {
|
|||
pub struct ModeViewDeclaration {
|
||||
pub ident: WithDecl<Ident>,
|
||||
pub typ: SubtypeIndication,
|
||||
pub is_token: TokenId,
|
||||
pub elements: Vec<ModeViewElement>,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ModeViewElement {
|
||||
pub names: IdentList,
|
||||
pub names: Vec<WithDecl<Ident>>,
|
||||
pub colon_token: TokenId,
|
||||
pub mode: ElementMode,
|
||||
}
|
||||
|
||||
|
|
@ -1248,7 +1343,7 @@ pub struct LabeledConcurrentStatement {
|
|||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct LibraryClause {
|
||||
pub name_list: IdentList,
|
||||
pub name_list: Vec<WithRef<Ident>>,
|
||||
}
|
||||
|
||||
/// Represents a token-separated list of some generic type `T`
|
||||
|
|
@ -1280,21 +1375,27 @@ impl SeparatedList<AssociationElement> {
|
|||
}
|
||||
}
|
||||
|
||||
pub type IdentList = SeparatedList<WithRef<Ident>>;
|
||||
pub type NameList = SeparatedList<WithTokenSpan<Name>>;
|
||||
impl<T> SeparatedList<T> {
|
||||
pub fn single(item: T) -> SeparatedList<T> {
|
||||
SeparatedList {
|
||||
items: vec![item],
|
||||
tokens: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// LRM 12.4. Use clauses
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct UseClause {
|
||||
pub name_list: NameList,
|
||||
pub name_list: Vec<WithTokenSpan<Name>>,
|
||||
}
|
||||
|
||||
/// LRM 13.4 Context clauses
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ContextReference {
|
||||
pub name_list: NameList,
|
||||
pub name_list: Vec<WithTokenSpan<Name>>,
|
||||
}
|
||||
|
||||
/// LRM 13.4 Context clauses
|
||||
|
|
@ -1311,6 +1412,7 @@ pub enum ContextItem {
|
|||
pub struct ContextDeclaration {
|
||||
pub ident: WithDecl<Ident>,
|
||||
pub items: ContextClause,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
|
|
@ -1341,6 +1443,7 @@ pub enum EntityAspect {
|
|||
}
|
||||
|
||||
/// LRM 7.3.2 Binding indication
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct BindingIndication {
|
||||
pub entity_aspect: Option<EntityAspect>,
|
||||
|
|
@ -1349,13 +1452,16 @@ pub struct BindingIndication {
|
|||
}
|
||||
|
||||
/// LRM 7.3 Configuration specification
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ComponentSpecification {
|
||||
pub instantiation_list: InstantiationList,
|
||||
pub colon_token: TokenId,
|
||||
pub component_name: WithTokenSpan<Name>,
|
||||
}
|
||||
|
||||
/// LRM 7.3.4 Verification unit binding indication
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct VUnitBindingIndication {
|
||||
pub vunit_list: Vec<WithTokenSpan<Name>>,
|
||||
|
|
@ -1368,17 +1474,11 @@ pub struct ConfigurationSpecification {
|
|||
pub spec: ComponentSpecification,
|
||||
pub bind_ind: BindingIndication,
|
||||
pub vunit_bind_inds: Vec<VUnitBindingIndication>,
|
||||
pub end_token: Option<TokenId>,
|
||||
}
|
||||
|
||||
/// LRM 3.4 Configuration declarations
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum ConfigurationDeclarativeItem {
|
||||
Use(UseClause),
|
||||
// @TODO attribute
|
||||
// @TODO group
|
||||
}
|
||||
|
||||
/// LRM 3.4 Configuration declarations
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ComponentConfiguration {
|
||||
pub spec: ComponentSpecification,
|
||||
|
|
@ -1395,6 +1495,7 @@ pub enum ConfigurationItem {
|
|||
}
|
||||
|
||||
/// LRM 3.4 Configuration declarations
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct BlockConfiguration {
|
||||
pub block_spec: WithTokenSpan<Name>,
|
||||
|
|
@ -1409,9 +1510,10 @@ pub struct ConfigurationDeclaration {
|
|||
pub context_clause: ContextClause,
|
||||
pub ident: WithDecl<Ident>,
|
||||
pub entity_name: WithTokenSpan<Name>,
|
||||
pub decl: Vec<ConfigurationDeclarativeItem>,
|
||||
pub decl: Vec<WithTokenSpan<Declaration>>,
|
||||
pub vunit_bind_inds: Vec<VUnitBindingIndication>,
|
||||
pub block_config: BlockConfiguration,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
|
|
@ -1424,10 +1526,20 @@ pub struct EntityDeclaration {
|
|||
pub generic_clause: Option<InterfaceList>,
|
||||
pub port_clause: Option<InterfaceList>,
|
||||
pub decl: Vec<WithTokenSpan<Declaration>>,
|
||||
pub begin_token: Option<TokenId>,
|
||||
pub statements: Vec<LabeledConcurrentStatement>,
|
||||
/// The `end` token from the declaration `*end* entity foo;`
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
impl EntityDeclaration {
|
||||
/// The `is` token from the declaration `entity foo *is*`
|
||||
pub fn is_token(&self) -> TokenId {
|
||||
self.span.start_token + 2
|
||||
}
|
||||
}
|
||||
|
||||
/// LRM 3.3 Architecture bodies
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
|
|
@ -1438,9 +1550,18 @@ pub struct ArchitectureBody {
|
|||
pub begin_token: TokenId,
|
||||
pub decl: Vec<WithTokenSpan<Declaration>>,
|
||||
pub statements: Vec<LabeledConcurrentStatement>,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
impl ArchitectureBody {
|
||||
/// Location of the `is` token from
|
||||
/// `architecture arch of ent is`
|
||||
pub fn is_token(&self) -> TokenId {
|
||||
self.span.start_token + 4
|
||||
}
|
||||
}
|
||||
|
||||
/// LRM 4.7 Package declarations
|
||||
#[with_token_span]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
|
|
@ -1449,6 +1570,7 @@ pub struct PackageDeclaration {
|
|||
pub ident: WithDecl<Ident>,
|
||||
pub generic_clause: Option<InterfaceList>,
|
||||
pub decl: Vec<WithTokenSpan<Declaration>>,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
|
|
@ -1459,6 +1581,7 @@ pub struct PackageBody {
|
|||
pub context_clause: ContextClause,
|
||||
pub ident: WithDecl<Ident>,
|
||||
pub decl: Vec<WithTokenSpan<Declaration>>,
|
||||
pub end_token: TokenId,
|
||||
pub end_ident_pos: Option<TokenId>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ impl Display for CallOrIndexed {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.name)?;
|
||||
let mut first = true;
|
||||
for param in &self.parameters {
|
||||
for param in &self.parameters.items {
|
||||
if first {
|
||||
write!(f, "({param}")?;
|
||||
} else {
|
||||
|
|
@ -365,7 +365,7 @@ impl Display for QualifiedExpression {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
match self.expr.item {
|
||||
Expression::Aggregate(..) => write!(f, "{}'{}", self.type_mark, self.expr),
|
||||
_ => write!(f, "{}'({})", self.type_mark, self.expr),
|
||||
_ => write!(f, "{}'{}", self.type_mark, self.expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -452,6 +452,7 @@ impl Display for Expression {
|
|||
Expression::Name(ref name) => write!(f, "{name}"),
|
||||
Expression::Literal(ref literal) => write!(f, "{literal}"),
|
||||
Expression::New(ref alloc) => write!(f, "new {alloc}"),
|
||||
Expression::Parenthesized(expr) => write!(f, "({expr})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -566,7 +567,7 @@ impl Display for ResolutionIndication {
|
|||
}
|
||||
ResolutionIndication::Record(elem_resolutions) => {
|
||||
let mut first = true;
|
||||
for elem_resolution in elem_resolutions {
|
||||
for elem_resolution in &elem_resolutions.item {
|
||||
if first {
|
||||
write!(f, "({elem_resolution}")?;
|
||||
} else {
|
||||
|
|
@ -580,16 +581,14 @@ impl Display for ResolutionIndication {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
ResolutionIndication::Unresolved => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SubtypeIndication {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
match self.resolution {
|
||||
ResolutionIndication::Unresolved => (),
|
||||
_ => write!(f, "{} ", self.resolution)?,
|
||||
if let Some(resolution) = &self.resolution {
|
||||
write!(f, "{resolution} ")?;
|
||||
}
|
||||
write!(f, "{}", self.type_mark)?;
|
||||
match self.constraint {
|
||||
|
|
@ -693,7 +692,7 @@ impl Display for TypeDefinition {
|
|||
}
|
||||
write!(f, "end units;")
|
||||
}
|
||||
TypeDefinition::Array(ref indexes, ref subtype_indication) => {
|
||||
TypeDefinition::Array(ref indexes, _, ref subtype_indication) => {
|
||||
write!(f, " is array (")?;
|
||||
let mut first = true;
|
||||
for index in indexes {
|
||||
|
|
@ -739,7 +738,7 @@ impl Display for TypeDefinition {
|
|||
|
||||
impl Display for TypeDeclaration {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
match self.def {
|
||||
match &self.def {
|
||||
TypeDefinition::Subtype(..) => write!(f, "subtype")?,
|
||||
_ => write!(f, "type")?,
|
||||
}
|
||||
|
|
@ -779,12 +778,20 @@ impl Display for ObjectDeclaration {
|
|||
|
||||
impl Display for FileDeclaration {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "file {} : {}", self.ident, self.subtype_indication)?;
|
||||
write!(
|
||||
f,
|
||||
"file {} : {}",
|
||||
self.idents
|
||||
.iter()
|
||||
.map(|ident| format!("{ident}"))
|
||||
.join(", "),
|
||||
self.subtype_indication
|
||||
)?;
|
||||
if let Some(ref expr) = self.open_info {
|
||||
write!(f, " open {expr}")?;
|
||||
write!(f, " open {}", expr.1)?;
|
||||
}
|
||||
match self.file_name {
|
||||
Some(ref expr) => write!(f, " is {expr};"),
|
||||
Some(ref expr) => write!(f, " is {};", expr.1),
|
||||
None => write!(f, ";"),
|
||||
}
|
||||
}
|
||||
|
|
@ -961,7 +968,7 @@ impl Display for ModeIndication {
|
|||
|
||||
impl Display for SimpleModeIndication {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
if let Some(mode) = self.mode {
|
||||
if let Some(mode) = &self.mode {
|
||||
write!(f, "{mode} ")?;
|
||||
}
|
||||
write!(f, "{}", self.subtype_indication)?;
|
||||
|
|
@ -982,7 +989,7 @@ impl Display for ModeViewIndication {
|
|||
ModeViewIndicationKind::Array => write!(f, "({})", self.name)?,
|
||||
ModeViewIndicationKind::Record => write!(f, "{}", self.name)?,
|
||||
}
|
||||
if let Some(typ) = &self.subtype_indication {
|
||||
if let Some((_, typ)) = &self.subtype_indication {
|
||||
write!(f, " of {typ}")?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -1046,7 +1053,7 @@ impl Display for InterfacePackageDeclaration {
|
|||
"package {} is new {}\n generic map (",
|
||||
self.ident, self.package_name
|
||||
)?;
|
||||
match &self.generic_map {
|
||||
match &self.generic_map.item {
|
||||
InterfacePackageGenericMapAspect::Map(assoc_list) => {
|
||||
let mut first = true;
|
||||
for assoc in &assoc_list.items {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ fn search_conditionals<T: Search>(
|
|||
return_if_found!(item.search(ctx, searcher));
|
||||
}
|
||||
}
|
||||
if let Some(expr) = else_item {
|
||||
if let Some((expr, _)) = else_item {
|
||||
return_if_found!(expr.search(ctx, searcher));
|
||||
}
|
||||
NotFound
|
||||
|
|
@ -191,7 +191,11 @@ fn search_alternatives<T: Search>(
|
|||
ctx: &dyn TokenAccess,
|
||||
) -> SearchResult {
|
||||
for alternative in alternatives.iter() {
|
||||
let Alternative { choices, item } = alternative;
|
||||
let Alternative {
|
||||
choices,
|
||||
item,
|
||||
span: _,
|
||||
} = &alternative;
|
||||
if item_before_choice {
|
||||
return_if_found!(item.search(ctx, searcher));
|
||||
return_if_found!(choices.search(ctx, searcher));
|
||||
|
|
@ -220,6 +224,7 @@ fn search_selection<T: Search>(
|
|||
searcher,
|
||||
ctx,
|
||||
));
|
||||
|
||||
NotFound
|
||||
}
|
||||
|
||||
|
|
@ -268,6 +273,12 @@ impl Search for WithTokenSpan<Choice> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Search for WithTokenSpan<ElementAssociation> {
|
||||
fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult {
|
||||
self.item.search(ctx, searcher)
|
||||
}
|
||||
}
|
||||
|
||||
impl Search for WithTokenSpan<Target> {
|
||||
fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult {
|
||||
match self.item {
|
||||
|
|
@ -362,7 +373,7 @@ impl Search for LabeledSequentialStatement {
|
|||
let LoopStatement {
|
||||
iteration_scheme,
|
||||
statements,
|
||||
end_label_pos: _,
|
||||
..
|
||||
} = loop_stmt;
|
||||
match iteration_scheme {
|
||||
Some(IterationScheme::For(ref index, ref drange)) => {
|
||||
|
|
@ -431,7 +442,8 @@ impl Search for GenerateBody {
|
|||
alternative_label,
|
||||
decl,
|
||||
statements,
|
||||
end_label_pos,
|
||||
end_label: end_label_pos,
|
||||
..
|
||||
} = self;
|
||||
if let Some(ref label) = alternative_label {
|
||||
return_if_found!(searcher
|
||||
|
|
@ -441,13 +453,15 @@ impl Search for GenerateBody {
|
|||
)
|
||||
.or_not_found());
|
||||
}
|
||||
return_if_found!(decl.search(ctx, searcher));
|
||||
if let Some((decl, _)) = decl {
|
||||
return_if_found!(decl.search(ctx, searcher));
|
||||
}
|
||||
return_if_found!(statements.search(ctx, searcher));
|
||||
|
||||
if let Some(ref label) = alternative_label {
|
||||
if let Some(end_label_pos) = end_label_pos {
|
||||
return_if_found!(searcher
|
||||
.search_pos_with_ref(ctx, end_label_pos, &label.decl)
|
||||
.search_pos_with_ref(ctx, ctx.get_pos(*end_label_pos), &label.decl)
|
||||
.or_not_found());
|
||||
}
|
||||
}
|
||||
|
|
@ -517,7 +531,9 @@ impl Search for LabeledConcurrentStatement {
|
|||
end_label_pos: _,
|
||||
..
|
||||
} = process;
|
||||
return_if_found!(sensitivity_list.search(ctx, searcher));
|
||||
if let Some(sensitivity_list) = sensitivity_list {
|
||||
return_if_found!(sensitivity_list.item.search(ctx, searcher));
|
||||
}
|
||||
return_if_found!(decl.search(ctx, searcher));
|
||||
return_if_found!(statements.search(ctx, searcher));
|
||||
}
|
||||
|
|
@ -687,7 +703,9 @@ impl Search for WithTokenSpan<SubtypeConstraint> {
|
|||
return_if_finished!(searcher.search_with_pos(ctx, &self.pos(ctx)));
|
||||
match self.item {
|
||||
SubtypeConstraint::Array(ref dranges, ref constraint) => {
|
||||
return_if_found!(dranges.search(ctx, searcher));
|
||||
for drange in dranges {
|
||||
return_if_found!(&drange.item.search(ctx, searcher));
|
||||
}
|
||||
if let Some(ref constraint) = constraint {
|
||||
return_if_found!(constraint.search(ctx, searcher));
|
||||
}
|
||||
|
|
@ -773,7 +791,7 @@ impl Search for TypeDeclaration {
|
|||
)
|
||||
.or_not_found());
|
||||
|
||||
match self.def {
|
||||
match &self.def {
|
||||
TypeDefinition::ProtectedBody(ref body) => {
|
||||
return_if_found!(body.decl.search(ctx, searcher));
|
||||
}
|
||||
|
|
@ -805,14 +823,14 @@ impl Search for TypeDeclaration {
|
|||
TypeDefinition::Access(ref subtype_indication) => {
|
||||
return_if_found!(subtype_indication.search(ctx, searcher));
|
||||
}
|
||||
TypeDefinition::Array(ref indexes, ref subtype_indication) => {
|
||||
TypeDefinition::Array(ref indexes, _, ref subtype_indication) => {
|
||||
for index in indexes.iter() {
|
||||
match index {
|
||||
ArrayIndex::IndexSubtypeDefintion(ref type_mark) => {
|
||||
return_if_found!(type_mark.search(ctx, searcher));
|
||||
}
|
||||
ArrayIndex::Discrete(ref drange) => {
|
||||
return_if_found!(drange.search(ctx, searcher));
|
||||
return_if_found!(drange.item.search(ctx, searcher));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -849,6 +867,7 @@ impl Search for TypeDeclaration {
|
|||
TypeDefinition::Physical(ref physical) => {
|
||||
let PhysicalTypeDeclaration {
|
||||
range,
|
||||
units_token: _,
|
||||
primary_unit,
|
||||
secondary_units,
|
||||
} = physical;
|
||||
|
|
@ -868,11 +887,13 @@ impl Search for TypeDeclaration {
|
|||
ctx,
|
||||
FoundDeclaration::new(
|
||||
&ident.decl,
|
||||
DeclarationItem::PhysicalTypeSecondary(ident, literal)
|
||||
DeclarationItem::PhysicalTypeSecondary(ident, &literal.item)
|
||||
)
|
||||
)
|
||||
.or_not_found());
|
||||
return_if_found!(searcher.search_ident_ref(ctx, &literal.unit).or_not_found());
|
||||
return_if_found!(searcher
|
||||
.search_ident_ref(ctx, &literal.item.unit)
|
||||
.or_not_found());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -917,6 +938,9 @@ fn search_pos_expr(
|
|||
}
|
||||
_ => NotFound,
|
||||
},
|
||||
Expression::Parenthesized(expr) => {
|
||||
search_pos_expr(ctx, &expr.span.pos(ctx), &expr.item, searcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -985,7 +1009,7 @@ impl Search for Waveform {
|
|||
return_if_found!(after.search(ctx, searcher));
|
||||
}
|
||||
}
|
||||
Waveform::Unaffected => {}
|
||||
Waveform::Unaffected(_) => {}
|
||||
}
|
||||
NotFound
|
||||
}
|
||||
|
|
@ -1078,8 +1102,8 @@ impl Search for Declaration {
|
|||
Declaration::Attribute(Attribute::Specification(AttributeSpecification {
|
||||
ident,
|
||||
entity_name,
|
||||
entity_class: _,
|
||||
expr,
|
||||
..
|
||||
})) => {
|
||||
return_if_found!(searcher.search_ident_ref(ctx, ident).or_not_found());
|
||||
if let EntityName::Name(EntityTag {
|
||||
|
|
@ -1108,10 +1132,10 @@ impl Search for Declaration {
|
|||
)
|
||||
.or_not_found());
|
||||
let AliasDeclaration {
|
||||
designator: _,
|
||||
subtype_indication,
|
||||
name,
|
||||
signature,
|
||||
..
|
||||
} = alias;
|
||||
return_if_found!(subtype_indication.search(ctx, searcher));
|
||||
return_if_found!(name.search(ctx, searcher));
|
||||
|
|
@ -1136,32 +1160,41 @@ impl Search for Declaration {
|
|||
)
|
||||
.or_not_found());
|
||||
let ComponentDeclaration {
|
||||
ident: _,
|
||||
generic_list,
|
||||
port_list,
|
||||
end_ident_pos: _,
|
||||
span: _,
|
||||
..
|
||||
} = component;
|
||||
return_if_found!(generic_list.search(ctx, searcher));
|
||||
return_if_found!(port_list.search(ctx, searcher));
|
||||
if let Some(generic_list) = generic_list {
|
||||
return_if_found!(generic_list.search(ctx, searcher));
|
||||
}
|
||||
if let Some(port_list) = port_list {
|
||||
return_if_found!(port_list.search(ctx, searcher));
|
||||
}
|
||||
}
|
||||
|
||||
Declaration::File(file) => {
|
||||
return_if_found!(searcher
|
||||
.search_decl(
|
||||
ctx,
|
||||
FoundDeclaration::new(&file.ident.decl, DeclarationItem::File(file))
|
||||
)
|
||||
.or_not_found());
|
||||
for ident in &file.idents {
|
||||
return_if_found!(searcher
|
||||
.search_decl(
|
||||
ctx,
|
||||
FoundDeclaration::new(&ident.decl, DeclarationItem::File(file))
|
||||
)
|
||||
.or_not_found());
|
||||
}
|
||||
let FileDeclaration {
|
||||
ident: _,
|
||||
idents: _,
|
||||
colon_token: _,
|
||||
subtype_indication,
|
||||
open_info,
|
||||
file_name,
|
||||
} = file;
|
||||
return_if_found!(subtype_indication.search(ctx, searcher));
|
||||
return_if_found!(open_info.search(ctx, searcher));
|
||||
return_if_found!(file_name.search(ctx, searcher));
|
||||
if let Some((_, open_info)) = open_info {
|
||||
return_if_found!(open_info.search(ctx, searcher));
|
||||
}
|
||||
if let Some((_, file_name)) = file_name {
|
||||
return_if_found!(file_name.search(ctx, searcher));
|
||||
}
|
||||
}
|
||||
|
||||
Declaration::Package(ref package_instance) => {
|
||||
|
|
@ -1178,12 +1211,7 @@ impl Search for Declaration {
|
|||
FoundDeclaration::new(&view.ident.decl, DeclarationItem::View(view))
|
||||
)
|
||||
.or_not_found());
|
||||
let ModeViewDeclaration {
|
||||
ident: _,
|
||||
typ,
|
||||
elements,
|
||||
end_ident_pos: _,
|
||||
} = view;
|
||||
let ModeViewDeclaration { typ, elements, .. } = view;
|
||||
return_if_found!(typ.search(ctx, searcher));
|
||||
return_if_found!(elements.search(ctx, searcher));
|
||||
}
|
||||
|
|
@ -1194,9 +1222,9 @@ impl Search for Declaration {
|
|||
|
||||
impl Search for ModeViewElement {
|
||||
fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult {
|
||||
for name in self.names.items.iter() {
|
||||
for name in self.names.iter() {
|
||||
return_if_found!(searcher
|
||||
.search_pos_with_ref(ctx, name.item.pos(ctx), &name.reference)
|
||||
.search_pos_with_ref(ctx, name.pos(ctx), &name.decl)
|
||||
.or_not_found());
|
||||
}
|
||||
NotFound
|
||||
|
|
@ -1223,7 +1251,9 @@ impl Search for SimpleModeIndication {
|
|||
impl Search for ModeViewIndication {
|
||||
fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult {
|
||||
return_if_found!(self.name.search(ctx, searcher));
|
||||
return_if_found!(self.subtype_indication.search(ctx, searcher));
|
||||
if let Some((_, subtype)) = &self.subtype_indication {
|
||||
return_if_found!(subtype.search(ctx, searcher));
|
||||
}
|
||||
NotFound
|
||||
}
|
||||
}
|
||||
|
|
@ -1284,7 +1314,7 @@ impl Search for InterfaceDeclaration {
|
|||
)
|
||||
.or_not_found());
|
||||
return_if_found!(package_instance.package_name.search(ctx, searcher));
|
||||
match package_instance.generic_map {
|
||||
match package_instance.generic_map.item {
|
||||
InterfacePackageGenericMapAspect::Map(ref generic_map) => {
|
||||
return_if_found!(generic_map.search(ctx, searcher));
|
||||
}
|
||||
|
|
@ -1355,7 +1385,7 @@ impl Search for SubprogramHeader {
|
|||
|
||||
impl Search for LibraryClause {
|
||||
fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult {
|
||||
for name in self.name_list.items.iter() {
|
||||
for name in self.name_list.iter() {
|
||||
return_if_found!(searcher
|
||||
.search_pos_with_ref(ctx, name.item.pos(ctx), &name.reference)
|
||||
.or_not_found());
|
||||
|
|
@ -1409,8 +1439,12 @@ impl Search for EntityDeclaration {
|
|||
FoundDeclaration::new(&self.ident.decl, DeclarationItem::Entity(self))
|
||||
)
|
||||
.or_not_found());
|
||||
return_if_found!(self.generic_clause.search(ctx, searcher));
|
||||
return_if_found!(self.port_clause.search(ctx, searcher));
|
||||
if let Some(clause) = &self.generic_clause {
|
||||
return_if_found!(clause.search(ctx, searcher));
|
||||
}
|
||||
if let Some(clause) = &self.port_clause {
|
||||
return_if_found!(clause.search(ctx, searcher));
|
||||
}
|
||||
return_if_found!(self.decl.search(ctx, searcher));
|
||||
self.statements.search(ctx, searcher)
|
||||
}
|
||||
|
|
@ -1502,10 +1536,9 @@ impl Search for ContextDeclaration {
|
|||
impl Search for CaseStatement {
|
||||
fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult {
|
||||
let CaseStatement {
|
||||
is_matching: _,
|
||||
expression,
|
||||
alternatives,
|
||||
end_label_pos: _,
|
||||
..
|
||||
} = self;
|
||||
return_if_found!(expression.search(ctx, searcher));
|
||||
return_if_found!(search_alternatives(alternatives, false, searcher, ctx));
|
||||
|
|
|
|||
|
|
@ -111,4 +111,11 @@ impl<T> WithTokenSpan<T> {
|
|||
span: self.span.start_with(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ref(&self) -> WithTokenSpan<&T> {
|
||||
WithTokenSpan {
|
||||
item: &self.item,
|
||||
span: self.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub fn to_simple_name(ctx: &dyn TokenAccess, name: WithTokenSpan<Name>) -> Diagn
|
|||
token: name.span.start_token,
|
||||
}),
|
||||
_ => Err(Diagnostic::new(
|
||||
&name.span.pos(ctx),
|
||||
name.span.pos(ctx),
|
||||
"Expected simple name",
|
||||
ErrorCode::SyntaxError,
|
||||
)),
|
||||
|
|
@ -84,6 +84,12 @@ impl<T: HasDesignator> HasDesignator for WithRef<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasIdent for WithRef<Ident> {
|
||||
fn ident(&self) -> &Ident {
|
||||
&self.item
|
||||
}
|
||||
}
|
||||
|
||||
impl Designator {
|
||||
pub fn into_ref(self) -> WithRef<Designator> {
|
||||
WithRef::new(self)
|
||||
|
|
@ -369,9 +375,9 @@ impl CallOrIndexed {
|
|||
ref mut parameters,
|
||||
} = self;
|
||||
|
||||
let mut indexes: Vec<Index> = Vec::with_capacity(parameters.len());
|
||||
let mut indexes: Vec<Index> = Vec::with_capacity(parameters.items.len());
|
||||
|
||||
for elem in parameters.iter_mut() {
|
||||
for elem in parameters.items.iter_mut() {
|
||||
if let ActualPart::Expression(ref mut expr) = &mut elem.actual.item {
|
||||
indexes.push(Index {
|
||||
pos: elem.actual.span,
|
||||
|
|
@ -385,6 +391,7 @@ impl CallOrIndexed {
|
|||
|
||||
pub fn could_be_indexed_name(&self) -> bool {
|
||||
self.parameters
|
||||
.items
|
||||
.iter()
|
||||
.all(|assoc| assoc.formal.is_none() && !matches!(assoc.actual.item, ActualPart::Open))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use crate::ast::search::{
|
|||
use crate::ast::{ConcurrentStatement, MapAspect, ObjectClass};
|
||||
use crate::named_entity::{AsUnique, Region};
|
||||
use crate::{
|
||||
named_entity, AnyEntKind, CompletionItem, Design, EntityId, Overloaded, Position, Source,
|
||||
TokenAccess,
|
||||
named_entity, AnyEntKind, CompletionItem, Design, EntityId, HasTokenSpan, Overloaded, Position,
|
||||
Source, TokenAccess,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ impl<'a> MapAspectSearcher<'a> {
|
|||
ctx: &dyn TokenAccess,
|
||||
kind: MapAspectKind,
|
||||
) -> bool {
|
||||
if !map.span(ctx).contains(self.cursor) {
|
||||
if !map.get_span(ctx).contains(self.cursor) {
|
||||
return false;
|
||||
}
|
||||
let formals_in_map: HashSet<EntityId> = HashSet::from_iter(map.formals().flatten());
|
||||
|
|
@ -156,7 +156,7 @@ fn extract_objects_with_class(region: &Region, object_class: ObjectClass) -> Vec
|
|||
/// # Arguments
|
||||
///
|
||||
/// * `object_class` - What to extract. `ObjectClass::Signal` extracts ports
|
||||
/// while `ObjectClass::Constant` extracts constants.
|
||||
/// while `ObjectClass::Constant` extracts constants.
|
||||
fn extract_port_or_generic_names(
|
||||
root: &DesignRoot,
|
||||
id: EntityId,
|
||||
|
|
|
|||
|
|
@ -350,6 +350,14 @@ impl<'a> ContentReader<'a> {
|
|||
pub fn peek_char(&self) -> Option<char> {
|
||||
self.get_char()
|
||||
}
|
||||
|
||||
pub fn value_at(&self, line: usize, start: usize, stop: usize) -> Option<Latin1String> {
|
||||
let line = self.contents.get_line(line)?;
|
||||
if stop > line.len() {
|
||||
return None;
|
||||
}
|
||||
Latin1String::from_utf8(&line[start..stop]).ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -169,6 +169,18 @@ impl Latin1String {
|
|||
}
|
||||
Ok(Latin1String::from_vec(latin1_bytes))
|
||||
}
|
||||
|
||||
pub fn push(&mut self, byte: u8) {
|
||||
self.bytes.push(byte)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.bytes.clear()
|
||||
}
|
||||
|
||||
pub fn append(&mut self, other: &mut Latin1String) {
|
||||
self.bytes.append(&mut other.bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Latin1String {
|
||||
|
|
|
|||
133
vhdl_lang/src/formatting/architecture.rs
Normal file
133
vhdl_lang/src/formatting/architecture.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::ArchitectureBody;
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::{indented, HasTokenSpan, TokenSpan};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_architecture(&self, arch: &ArchitectureBody, buffer: &mut Buffer) {
|
||||
self.format_context_clause(&arch.context_clause, buffer);
|
||||
if let Some(item) = arch.context_clause.last() {
|
||||
self.line_break_preserve_whitespace(item.span().end_token, buffer);
|
||||
}
|
||||
let span = arch.span();
|
||||
// architecture <ident> of <ident> is
|
||||
self.format_token_span(TokenSpan::new(span.start_token, arch.is_token()), buffer);
|
||||
indented!(buffer, { self.format_declarations(&arch.decl, buffer) });
|
||||
buffer.line_break();
|
||||
self.format_token_id(arch.begin_token, buffer);
|
||||
indented!(buffer, {
|
||||
self.format_concurrent_statements(&arch.statements, buffer);
|
||||
});
|
||||
buffer.line_break();
|
||||
// end [architecture] [name];
|
||||
self.format_token_span(TokenSpan::new(arch.end_token, span.end_token - 1), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::syntax::test::Code;
|
||||
use vhdl_lang::formatting::test_utils::check_formatted;
|
||||
|
||||
fn check_architecture_formatted(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::architecture_body,
|
||||
|formatter, arch, buffer| formatter.format_architecture(arch, buffer),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_empty_architecture() {
|
||||
check_architecture_formatted(
|
||||
"\
|
||||
architecture foo of bar is
|
||||
begin
|
||||
end foo;",
|
||||
);
|
||||
|
||||
check_architecture_formatted(
|
||||
"\
|
||||
architecture foo of bar is
|
||||
begin
|
||||
end architecture foo;",
|
||||
);
|
||||
|
||||
check_architecture_formatted(
|
||||
"\
|
||||
architecture foo of bar is
|
||||
begin
|
||||
end;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_architecture_with_declarations() {
|
||||
check_architecture_formatted(
|
||||
"\
|
||||
architecture foo of bar is
|
||||
constant x: foo := bar;
|
||||
begin
|
||||
end foo;",
|
||||
);
|
||||
|
||||
check_architecture_formatted(
|
||||
"\
|
||||
architecture foo of bar is
|
||||
constant x: foo := bar;
|
||||
signal y: bar := foobar;
|
||||
begin
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_full_architecture() {
|
||||
check_architecture_formatted(
|
||||
"\
|
||||
architecture foo of bar is
|
||||
constant x: foo := bar;
|
||||
signal y: bar := foobar;
|
||||
begin
|
||||
bar: process(clk) is
|
||||
variable z: baz;
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if rst = '1' then
|
||||
foo <= '0';
|
||||
else
|
||||
foo <= bar and baz;
|
||||
end if;
|
||||
end if;
|
||||
end process bar;
|
||||
y <= x; -- An assignment
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_architecture_preserve_whitespace() {
|
||||
check_architecture_formatted(
|
||||
"\
|
||||
architecture foo of bar is
|
||||
constant x: foo := bar;
|
||||
constant baz: char := '1';
|
||||
|
||||
signal y: bar := foobar;
|
||||
signal foobar: std_logic := 'Z';
|
||||
|
||||
shared variable sv: natural;
|
||||
begin
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
}
|
||||
330
vhdl_lang/src/formatting/buffer.rs
Normal file
330
vhdl_lang/src/formatting/buffer.rs
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::syntax::{Comment, Value};
|
||||
use crate::{kind_str, Token};
|
||||
use std::cmp::max;
|
||||
use std::iter;
|
||||
|
||||
/// The Buffer is the (mostly) mutable object used to write tokens to a string.
|
||||
/// It operates mostly on tokens and is capable of indenting,
|
||||
/// de-indenting and keeping the indentation level.
|
||||
pub struct Buffer {
|
||||
inner: String,
|
||||
/// insert an extra newline before pushing a token.
|
||||
/// This is relevant when there is a trailing comment
|
||||
insert_extra_newline: bool,
|
||||
/// The current indentation level
|
||||
indentation: usize,
|
||||
/// The char used for indentation
|
||||
indent_char: char,
|
||||
/// The width used at each indentation level
|
||||
indent_width: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new() -> Buffer {
|
||||
Buffer {
|
||||
inner: String::new(),
|
||||
insert_extra_newline: false,
|
||||
indentation: 0,
|
||||
indent_char: ' ',
|
||||
indent_width: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Buffer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether a leading comment is on the same line as the token, i.e.,
|
||||
/// check the case
|
||||
/// ```vhdl
|
||||
/// /* some comment */ token
|
||||
/// ```
|
||||
fn leading_comment_is_on_token_line(comment: &Comment, token: &Token) -> bool {
|
||||
if !comment.multi_line {
|
||||
return false;
|
||||
}
|
||||
if comment.range.start.line != comment.range.end.line {
|
||||
return false;
|
||||
}
|
||||
token.pos.start().line == comment.range.start.line
|
||||
}
|
||||
|
||||
impl From<Buffer> for String {
|
||||
fn from(value: Buffer) -> Self {
|
||||
value.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.inner.as_str()
|
||||
}
|
||||
|
||||
/// pushes a whitespace character to the buffer
|
||||
pub fn push_whitespace(&mut self) {
|
||||
if !self.insert_extra_newline {
|
||||
self.push_ch(' ');
|
||||
}
|
||||
}
|
||||
|
||||
fn format_comment(&mut self, comment: &Comment) {
|
||||
if !comment.multi_line {
|
||||
self.push_str("--");
|
||||
self.push_str(comment.value.trim_end())
|
||||
} else {
|
||||
self.push_str("/*");
|
||||
self.push_str(&comment.value);
|
||||
self.push_str("*/");
|
||||
}
|
||||
}
|
||||
|
||||
fn format_leading_comments(&mut self, comments: &[Comment]) {
|
||||
for (i, comment) in comments.iter().enumerate() {
|
||||
self.format_comment(comment);
|
||||
if let Some(next_comment) = comments.get(i + 1) {
|
||||
let number_of_line_breaks =
|
||||
max(next_comment.range.start.line - comment.range.end.line, 1);
|
||||
self.line_breaks(number_of_line_breaks);
|
||||
} else {
|
||||
self.line_break();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn indent(&mut self) {
|
||||
self.inner
|
||||
.extend(iter::repeat(self.indent_char).take(self.indent_width * self.indentation));
|
||||
}
|
||||
|
||||
/// Push a token to this buffer.
|
||||
/// This takes care of all the leading and trailing comments attached to that token.
|
||||
pub fn push_token(&mut self, token: &Token) {
|
||||
if self.insert_extra_newline {
|
||||
self.line_break();
|
||||
}
|
||||
self.insert_extra_newline = false;
|
||||
if let Some(comments) = &token.comments {
|
||||
// This is for example the case for situations like
|
||||
// some_token /* comment in between */ some_other token
|
||||
if comments.leading.len() == 1
|
||||
&& leading_comment_is_on_token_line(&comments.leading[0], token)
|
||||
{
|
||||
self.format_comment(&comments.leading[0]);
|
||||
self.push_ch(' ');
|
||||
} else if !comments.leading.is_empty() {
|
||||
self.format_leading_comments(comments.leading.as_slice());
|
||||
}
|
||||
}
|
||||
match &token.value {
|
||||
Value::Identifier(ident) => self.push_str(&ident.to_string()),
|
||||
Value::String(string) => {
|
||||
self.push_ch('"');
|
||||
for byte in &string.bytes {
|
||||
if *byte == b'"' {
|
||||
self.push_ch('"');
|
||||
self.push_ch('"');
|
||||
} else {
|
||||
self.push_ch(*byte as char);
|
||||
}
|
||||
}
|
||||
self.push_ch('"');
|
||||
}
|
||||
Value::BitString(value, _) => self.push_str(&value.to_string()),
|
||||
Value::AbstractLiteral(value, _) => self.push_str(&value.to_string()),
|
||||
Value::Character(char) => {
|
||||
self.push_ch('\'');
|
||||
self.push_ch(*char as char);
|
||||
self.push_ch('\'');
|
||||
}
|
||||
Value::Text(text) => self.push_str(&text.to_string()),
|
||||
Value::None => self.push_str(kind_str(token.kind)),
|
||||
}
|
||||
if let Some(comments) = &token.comments {
|
||||
if let Some(trailing_comment) = &comments.trailing {
|
||||
self.push_ch(' ');
|
||||
self.format_comment(trailing_comment);
|
||||
self.insert_extra_newline = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_str(&mut self, value: &str) {
|
||||
self.inner.push_str(value);
|
||||
}
|
||||
|
||||
fn push_ch(&mut self, char: char) {
|
||||
self.inner.push(char);
|
||||
}
|
||||
|
||||
/// Increase the indentation level.
|
||||
/// After this call, all new-line pushes will be preceded by an indentation,
|
||||
/// specified via the `indent_char` and `indent_width` properties.
|
||||
///
|
||||
/// This call should always be matched with a `decrease_indent` call.
|
||||
/// There is also the `indented` macro that combines the two calls.
|
||||
pub fn increase_indent(&mut self) {
|
||||
self.indentation += 1;
|
||||
}
|
||||
|
||||
pub fn decrease_indent(&mut self) {
|
||||
self.indentation -= 1;
|
||||
}
|
||||
|
||||
/// Inserts a line break (i.e., newline) at the current position
|
||||
pub fn line_break(&mut self) {
|
||||
self.insert_extra_newline = false;
|
||||
self.push_ch('\n');
|
||||
self.indent();
|
||||
}
|
||||
|
||||
/// Inserts multiple line breaks.
|
||||
/// Note that this method must always be used (i.e., is different from
|
||||
/// multiple `line_break` calls) as this method only indents the last line break
|
||||
pub fn line_breaks(&mut self, count: u32) {
|
||||
self.insert_extra_newline = false;
|
||||
for _ in 0..count {
|
||||
self.push_ch('\n');
|
||||
}
|
||||
self.indent();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::analysis::tests::Code;
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use std::iter::zip;
|
||||
|
||||
fn check_token_formatted(input: &str, expected: &[&str]) {
|
||||
let code = Code::new(input);
|
||||
let tokens = code.tokenize();
|
||||
for (token, expected) in zip(tokens, expected) {
|
||||
let mut buffer = Buffer::new();
|
||||
buffer.push_token(&token);
|
||||
assert_eq!(buffer.as_str(), *expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_simple_token() {
|
||||
check_token_formatted("entity", &["entity"]);
|
||||
check_token_formatted("foobar", &["foobar"]);
|
||||
check_token_formatted("1 23 4E5 4e5", &["1", "23", "4E5", "4e5"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserves_identifier_casing() {
|
||||
check_token_formatted("FooBar foobar", &["FooBar", "foobar"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character_formatting() {
|
||||
check_token_formatted("'a' 'Z' '''", &["'a'", "'Z'", "'''"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_formatting() {
|
||||
check_token_formatted(
|
||||
r#""ABC" "" "DEF" """" "Hello "" ""#,
|
||||
&["\"ABC\"", "\"\"", "\"DEF\"", "\"\"\"\"", "\"Hello \"\" \""],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bit_string_formatting() {
|
||||
check_token_formatted(r#"B"10" 20B"8" X"2F""#, &["B\"10\"", "20B\"8\"", "X\"2F\""]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_comment() {
|
||||
check_token_formatted(
|
||||
"\
|
||||
-- I am a comment
|
||||
foobar
|
||||
",
|
||||
&["\
|
||||
-- I am a comment
|
||||
foobar"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_leading_comments() {
|
||||
check_token_formatted(
|
||||
"\
|
||||
-- I am a comment
|
||||
-- So am I
|
||||
foobar
|
||||
",
|
||||
&["\
|
||||
-- I am a comment
|
||||
-- So am I
|
||||
foobar"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_comments() {
|
||||
check_token_formatted(
|
||||
"\
|
||||
foobar --After foobar comes foobaz
|
||||
",
|
||||
&["foobar --After foobar comes foobaz"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_multiline_comment() {
|
||||
check_token_formatted(
|
||||
"\
|
||||
/** Some documentation.
|
||||
* This is a token named 'entity'
|
||||
*/
|
||||
entity
|
||||
",
|
||||
&["\
|
||||
/** Some documentation.
|
||||
* This is a token named 'entity'
|
||||
*/
|
||||
entity"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_comment_and_simple_comment() {
|
||||
check_token_formatted(
|
||||
"\
|
||||
/* I am a multiline comment */
|
||||
-- And I am a single line comment
|
||||
entity
|
||||
",
|
||||
&["\
|
||||
/* I am a multiline comment */
|
||||
-- And I am a single line comment
|
||||
entity"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_comment_and_trailing_comment() {
|
||||
check_token_formatted(
|
||||
"\
|
||||
-- Leading comment
|
||||
entity -- Trailing comment
|
||||
",
|
||||
&["\
|
||||
-- Leading comment
|
||||
entity -- Trailing comment"],
|
||||
);
|
||||
}
|
||||
}
|
||||
928
vhdl_lang/src/formatting/concurrent_statement.rs
Normal file
928
vhdl_lang/src/formatting/concurrent_statement.rs
Normal file
|
|
@ -0,0 +1,928 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{
|
||||
AssignmentRightHand, BlockStatement, CaseGenerateStatement, ConcurrentAssertStatement,
|
||||
ConcurrentSignalAssignment, ConcurrentStatement, Conditionals, ElementAssociation,
|
||||
ForGenerateStatement, GenerateBody, Ident, IfGenerateStatement, InstantiatedUnit,
|
||||
InstantiationStatement, LabeledConcurrentStatement, ProcessStatement, SensitivityList, Target,
|
||||
Waveform, WaveformElement,
|
||||
};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::syntax::Kind;
|
||||
use crate::{HasTokenSpan, TokenAccess};
|
||||
use vhdl_lang::ast::{Alternative, AssertStatement, ConcurrentProcedureCall};
|
||||
use vhdl_lang::{indented, TokenSpan};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn join_on_newline<T>(
|
||||
&self,
|
||||
items: &[T],
|
||||
joiner: impl Fn(&Self, &T, &mut Buffer),
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
for item in items {
|
||||
buffer.line_break();
|
||||
joiner(self, item, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_concurrent_statements(
|
||||
&self,
|
||||
statements: &[LabeledConcurrentStatement],
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if statements.is_empty() {
|
||||
return;
|
||||
}
|
||||
buffer.line_break();
|
||||
for (i, item) in statements.iter().enumerate() {
|
||||
self.format_labeled_concurrent_statement(item, buffer);
|
||||
if i < statements.len() - 1 {
|
||||
self.line_break_preserve_whitespace(item.statement.get_end_token(), buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_optional_label(&self, label: Option<&Ident>, buffer: &mut Buffer) {
|
||||
if let Some(label) = label {
|
||||
self.format_token_id(label.token, buffer);
|
||||
// :
|
||||
self.format_token_id(label.token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_labeled_concurrent_statement(
|
||||
&self,
|
||||
statement: &LabeledConcurrentStatement,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_optional_label(statement.label.tree.as_ref(), buffer);
|
||||
self.format_concurrent_statement(&statement.statement, buffer);
|
||||
}
|
||||
|
||||
pub fn format_concurrent_statement(
|
||||
&self,
|
||||
statement: &WithTokenSpan<ConcurrentStatement>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
use ConcurrentStatement::*;
|
||||
let span = statement.span;
|
||||
match &statement.item {
|
||||
ProcedureCall(call) => self.format_procedure_call(call, span, buffer),
|
||||
Block(block) => self.format_block_statement(block, span, buffer),
|
||||
Process(process) => self.format_process_statement(process, span, buffer),
|
||||
Assert(assert) => self.format_concurrent_assert_statement(assert, span, buffer),
|
||||
Assignment(assignment) => self.format_assignment_statement(assignment, span, buffer),
|
||||
Instance(instantiation_statement) => {
|
||||
self.format_instantiation_statement(instantiation_statement, span, buffer)
|
||||
}
|
||||
ForGenerate(for_generate) => {
|
||||
self.format_for_generate_statement(for_generate, span, buffer)
|
||||
}
|
||||
IfGenerate(if_generate) => self.format_if_generate_statement(if_generate, span, buffer),
|
||||
CaseGenerate(case_generate) => {
|
||||
self.format_case_generate_statement(case_generate, span, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_procedure_call(
|
||||
&self,
|
||||
call: &ConcurrentProcedureCall,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if call.postponed {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_call_or_indexed(&call.call.item, call.call.span, buffer);
|
||||
// ;
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_block_statement(
|
||||
&self,
|
||||
block: &BlockStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// block
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
if let Some(guard_condition) = &block.guard_condition {
|
||||
self.format_token_id(guard_condition.span.start_token - 1, buffer);
|
||||
self.format_expression(guard_condition.as_ref(), buffer);
|
||||
self.format_token_id(guard_condition.span.end_token + 1, buffer);
|
||||
}
|
||||
if let Some(is_token) = block.is_token {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(is_token, buffer);
|
||||
}
|
||||
indented!(buffer, {
|
||||
if let Some(generic_clause) = &block.header.generic_clause {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(generic_clause, buffer);
|
||||
}
|
||||
if let Some(generic_map) = &block.header.generic_map {
|
||||
buffer.line_break();
|
||||
self.format_map_aspect(generic_map, buffer);
|
||||
// ;
|
||||
self.format_token_id(generic_map.span.end_token + 1, buffer);
|
||||
}
|
||||
if let Some(port_clause) = &block.header.port_clause {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(port_clause, buffer);
|
||||
}
|
||||
if let Some(port_map) = &block.header.port_map {
|
||||
buffer.line_break();
|
||||
self.format_map_aspect(port_map, buffer);
|
||||
// ;
|
||||
self.format_token_id(port_map.span.end_token + 1, buffer);
|
||||
}
|
||||
self.format_declarations(&block.decl, buffer);
|
||||
});
|
||||
buffer.line_break();
|
||||
self.format_token_id(block.begin_token, buffer);
|
||||
buffer.line_break();
|
||||
indented!(buffer, {
|
||||
self.format_concurrent_statements(&block.statements, buffer)
|
||||
});
|
||||
self.format_token_span(
|
||||
TokenSpan::new(block.end_token, block.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
// ;
|
||||
self.format_token_id(block.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_process_statement(
|
||||
&self,
|
||||
process: &ProcessStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.token_with_opt_postponed(span, buffer);
|
||||
if let Some(sensitivity_list) = &process.sensitivity_list {
|
||||
match &sensitivity_list.item {
|
||||
SensitivityList::Names(names) => {
|
||||
self.format_token_id(sensitivity_list.span.start_token, buffer);
|
||||
self.format_name_list(buffer, names);
|
||||
self.format_token_id(sensitivity_list.span.end_token, buffer);
|
||||
}
|
||||
SensitivityList::All => self.join_token_span(sensitivity_list.span, buffer),
|
||||
}
|
||||
}
|
||||
if let Some(is_token) = process.is_token {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(is_token, buffer);
|
||||
}
|
||||
indented!(buffer, { self.format_declarations(&process.decl, buffer) });
|
||||
buffer.line_break();
|
||||
self.format_token_id(process.begin_token, buffer);
|
||||
self.format_sequential_statements(&process.statements, buffer);
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(process.end_token, process.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
// ;
|
||||
self.format_token_id(process.span.end_token, buffer);
|
||||
}
|
||||
|
||||
fn token_with_opt_postponed(&self, span: TokenSpan, buffer: &mut Buffer) {
|
||||
if self.tokens.index(span.start_token).kind == Kind::Postponed {
|
||||
// postponed <x>
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, span.start_token + 1),
|
||||
buffer,
|
||||
);
|
||||
} else {
|
||||
// <x>
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_concurrent_assert_statement(
|
||||
&self,
|
||||
statement: &ConcurrentAssertStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.token_with_opt_postponed(span, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_assert_statement(&statement.statement, buffer);
|
||||
// ;
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_assert_statement(&self, assert_statement: &AssertStatement, buffer: &mut Buffer) {
|
||||
self.format_expression(assert_statement.condition.as_ref(), buffer);
|
||||
if let Some(report) = &assert_statement.report {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(report.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(report.as_ref(), buffer);
|
||||
}
|
||||
self.format_opt_severity(assert_statement.severity.as_ref(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_assignment_statement(
|
||||
&self,
|
||||
assignment_statement: &ConcurrentSignalAssignment,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if let AssignmentRightHand::Selected(selected) = &assignment_statement.assignment.rhs {
|
||||
// with
|
||||
self.format_token_id(selected.expression.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(selected.expression.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
// select
|
||||
self.format_token_id(selected.expression.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_target(&assignment_statement.assignment.target, buffer);
|
||||
buffer.push_whitespace();
|
||||
// <=
|
||||
self.format_token_id(
|
||||
assignment_statement.assignment.target.span.end_token + 1,
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
if let Some(mechanism) = &assignment_statement.assignment.delay_mechanism {
|
||||
self.format_delay_mechanism(mechanism, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_assignment_right_hand(
|
||||
&assignment_statement.assignment.rhs,
|
||||
Self::format_waveform,
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_assignment_right_hand<T>(
|
||||
&self,
|
||||
right_hand: &AssignmentRightHand<T>,
|
||||
formatter: impl Fn(&Self, &T, &mut Buffer),
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
use AssignmentRightHand::*;
|
||||
match right_hand {
|
||||
Simple(simple) => formatter(self, simple, buffer),
|
||||
Conditional(conditionals) => {
|
||||
self.format_assignment_right_hand_conditionals(conditionals, formatter, buffer)
|
||||
}
|
||||
Selected(selection) => {
|
||||
for alternative in &selection.alternatives {
|
||||
self.format_alternative(alternative, &formatter, buffer);
|
||||
if self
|
||||
.tokens
|
||||
.get_token(alternative.span.end_token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Comma)
|
||||
{
|
||||
self.format_token_id(alternative.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_alternative<T>(
|
||||
&self,
|
||||
alternative: &Alternative<T>,
|
||||
formatter: &impl Fn(&Self, &T, &mut Buffer),
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
formatter(self, &alternative.item, buffer);
|
||||
buffer.push_whitespace();
|
||||
for (i, choice) in alternative.choices.iter().enumerate() {
|
||||
if i == 0 {
|
||||
// when
|
||||
self.format_token_id(choice.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_choice(choice, buffer);
|
||||
if i < alternative.choices.len() - 1 {
|
||||
buffer.push_whitespace();
|
||||
// |
|
||||
self.format_token_id(choice.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_assignment_right_hand_conditionals<T>(
|
||||
&self,
|
||||
conditionals: &Conditionals<T>,
|
||||
formatter: impl Fn(&Self, &T, &mut Buffer),
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
for cond in &conditionals.conditionals {
|
||||
// item
|
||||
formatter(self, &cond.item, buffer);
|
||||
let condition = &cond.condition;
|
||||
buffer.push_whitespace();
|
||||
// when
|
||||
self.format_token_id(condition.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(condition.as_ref(), buffer);
|
||||
// [else]
|
||||
if self
|
||||
.tokens
|
||||
.get_token(cond.condition.span.end_token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Else)
|
||||
{
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(cond.condition.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
if let Some((statements, _)) = &conditionals.else_item {
|
||||
// else handled above
|
||||
formatter(self, statements, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_waveform(&self, waveform: &Waveform, buffer: &mut Buffer) {
|
||||
match waveform {
|
||||
Waveform::Elements(elements) => {
|
||||
for (i, element) in elements.iter().enumerate() {
|
||||
self.format_waveform_element(element, buffer);
|
||||
if i < elements.len() - 1 {
|
||||
self.format_token_id(element.get_end_token() + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
Waveform::Unaffected(token) => self.format_token_id(*token, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_waveform_element(&self, element: &WaveformElement, buffer: &mut Buffer) {
|
||||
self.format_expression(element.value.as_ref(), buffer);
|
||||
if let Some(after) = &element.after {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(after.get_start_token() - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(after.as_ref(), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_target(&self, target: &WithTokenSpan<Target>, buffer: &mut Buffer) {
|
||||
match &target.item {
|
||||
Target::Name(name) => self.format_name(WithTokenSpan::new(name, target.span), buffer),
|
||||
Target::Aggregate(associations) => {
|
||||
self.format_target_aggregate(associations, target.span, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_target_aggregate(
|
||||
&self,
|
||||
associations: &[WithTokenSpan<ElementAssociation>],
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// (
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
self.format_element_associations(associations, buffer);
|
||||
// )
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_instantiation_statement(
|
||||
&self,
|
||||
statement: &InstantiationStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if matches!(
|
||||
self.tokens.index(span.start_token).kind,
|
||||
Kind::Component | Kind::Entity | Kind::Configuration
|
||||
) {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
match &statement.unit {
|
||||
InstantiatedUnit::Component(name) | InstantiatedUnit::Configuration(name) => {
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
}
|
||||
InstantiatedUnit::Entity(name, architecture) => {
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
if let Some(arch) = architecture {
|
||||
self.join_token_span(
|
||||
TokenSpan::new(arch.item.token - 1, arch.item.token + 1),
|
||||
buffer,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(generic_map) = &statement.generic_map {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_map_aspect(generic_map, buffer);
|
||||
});
|
||||
}
|
||||
if let Some(port_map) = &statement.port_map {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_map_aspect(port_map, buffer);
|
||||
});
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_for_generate_statement(
|
||||
&self,
|
||||
statement: &ForGenerateStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// for
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
// index
|
||||
self.format_ident(&statement.index_name, buffer);
|
||||
buffer.push_whitespace();
|
||||
// in
|
||||
self.format_token_id(statement.index_name.tree.token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_discrete_range(&statement.discrete_range, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(statement.generate_token, buffer);
|
||||
self.format_generate_body(&statement.body, buffer);
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(statement.end_token, span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_if_generate_statement(
|
||||
&self,
|
||||
statement: &IfGenerateStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
for cond in &statement.conds.conditionals {
|
||||
let condition = &cond.condition;
|
||||
if let Some(label) = &cond.item.alternative_label {
|
||||
// if | elsif
|
||||
self.format_token_id(label.tree.token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
// label
|
||||
self.format_token_id(label.tree.token, buffer);
|
||||
// :
|
||||
self.format_token_id(label.tree.token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
} else {
|
||||
self.format_token_id(condition.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_expression(condition.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
// generate
|
||||
self.format_token_id(condition.span.end_token + 1, buffer);
|
||||
self.format_generate_body(&cond.item, buffer);
|
||||
buffer.line_break();
|
||||
}
|
||||
if let Some((statements, token)) = &statement.conds.else_item {
|
||||
if let Some(label) = &statements.alternative_label {
|
||||
// else
|
||||
self.format_token_id(label.tree.token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
// label
|
||||
self.format_token_id(label.tree.token, buffer);
|
||||
// :
|
||||
self.format_token_id(label.tree.token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
// generate
|
||||
self.format_token_id(label.tree.token + 2, buffer);
|
||||
} else {
|
||||
// else
|
||||
self.format_token_id(*token, buffer);
|
||||
buffer.push_whitespace();
|
||||
// generate
|
||||
self.format_token_id(*token + 1, buffer);
|
||||
}
|
||||
self.format_generate_body(statements, buffer);
|
||||
buffer.line_break();
|
||||
}
|
||||
if statement.end_label_pos.is_some() {
|
||||
// end if <label>
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.end_token - 3, span.end_token - 1),
|
||||
buffer,
|
||||
)
|
||||
} else {
|
||||
// end if
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.end_token - 2, span.end_token - 1),
|
||||
buffer,
|
||||
)
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_case_generate_statement(
|
||||
&self,
|
||||
statement: &CaseGenerateStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// case
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(statement.sels.expression.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
// generate
|
||||
self.format_token_id(statement.sels.expression.span.end_token + 1, buffer);
|
||||
indented!(buffer, {
|
||||
for alternative in &statement.sels.alternatives {
|
||||
buffer.line_break();
|
||||
for (i, choice) in alternative.choices.iter().enumerate() {
|
||||
if i == 0 {
|
||||
if let Some(label) = &alternative.item.alternative_label {
|
||||
// when
|
||||
self.format_token_id(label.tree.token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
// <ident>
|
||||
self.format_token_id(label.tree.token, buffer);
|
||||
// :
|
||||
self.format_token_id(label.tree.token + 1, buffer);
|
||||
} else {
|
||||
// when
|
||||
self.format_token_id(choice.span.start_token - 1, buffer);
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_choice(choice, buffer);
|
||||
if i < alternative.choices.len() - 1 {
|
||||
buffer.push_whitespace();
|
||||
// |
|
||||
self.format_token_id(choice.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
if i == alternative.choices.len() - 1 {
|
||||
buffer.push_whitespace();
|
||||
// =>
|
||||
self.format_token_id(choice.span.end_token + 1, buffer);
|
||||
}
|
||||
}
|
||||
self.format_generate_body(&alternative.item, buffer);
|
||||
}
|
||||
});
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(statement.end_token, span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_generate_body(&self, generate_body: &GenerateBody, buffer: &mut Buffer) {
|
||||
if let Some((decl, begin_token)) = &generate_body.decl {
|
||||
indented!(buffer, { self.format_declarations(decl, buffer) });
|
||||
buffer.line_break();
|
||||
self.format_token_id(*begin_token, buffer);
|
||||
}
|
||||
indented!(buffer, {
|
||||
self.format_concurrent_statements(&generate_body.statements, buffer)
|
||||
});
|
||||
if let Some(end_token) = generate_body.end_token {
|
||||
buffer.line_break();
|
||||
self.format_token_id(end_token, buffer);
|
||||
if let Some(token) = generate_body.end_label {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(token, buffer);
|
||||
// ;
|
||||
self.format_token_id(token + 1, buffer);
|
||||
} else {
|
||||
// ;
|
||||
self.format_token_id(end_token + 1, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::formatting::test_utils::check_formatted;
|
||||
use crate::syntax::test::Code;
|
||||
|
||||
fn check_statement(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::concurrent_statement,
|
||||
|formatter, ast, buffer| formatter.format_labeled_concurrent_statement(ast, buffer),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn procedure_calls() {
|
||||
check_statement("foo;");
|
||||
check_statement("foo(clk);");
|
||||
check_statement("foo(clk, bar);");
|
||||
check_statement("foo(0);");
|
||||
check_statement("foo(arg => 0);");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blocks() {
|
||||
check_statement(
|
||||
"\
|
||||
name: block
|
||||
begin
|
||||
end block name;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
name: block is
|
||||
begin
|
||||
end block name;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
name: block(cond = true)
|
||||
begin
|
||||
end block;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
name: block(cond = true) is
|
||||
begin
|
||||
end block;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
block(cond = true) is
|
||||
begin
|
||||
end block;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
name: block is
|
||||
generic (
|
||||
gen: integer := 1
|
||||
);
|
||||
generic map (
|
||||
gen => 1
|
||||
);
|
||||
port (
|
||||
prt: integer := 1
|
||||
);
|
||||
port map (
|
||||
prt => 2
|
||||
);
|
||||
begin
|
||||
end block;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_processes() {
|
||||
check_statement(
|
||||
"\
|
||||
process
|
||||
begin
|
||||
end process;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
name: process is
|
||||
begin
|
||||
end process name;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
postponed process
|
||||
begin
|
||||
end process;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
postponed process
|
||||
begin
|
||||
end postponed process;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
process(clk, vec(1)) is
|
||||
begin
|
||||
end process;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
process(all) is
|
||||
variable foo: boolean;
|
||||
begin
|
||||
end process;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_assert() {
|
||||
check_statement("assert false;");
|
||||
check_statement("assert cond = true;");
|
||||
check_statement("postponed assert cond = true;");
|
||||
check_statement("assert false report \"message\" severity error;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_signal_assignment() {
|
||||
check_statement("foo <= bar(2 to 3);");
|
||||
check_statement("x <= bar(1 to 3) after 2 ns;");
|
||||
check_statement("foo <= bar(1 to 3) after 2 ns, expr after 1 ns;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_simple_instantiation_statement() {
|
||||
check_statement("inst: component lib.foo.bar;");
|
||||
check_statement("inst: configuration lib.foo.bar;");
|
||||
check_statement("inst: entity lib.foo.bar;");
|
||||
check_statement("inst: entity lib.foo.bar(arch);");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_instantiation_statement_generic_map() {
|
||||
check_statement(
|
||||
"\
|
||||
inst: component lib.foo.bar
|
||||
generic map (
|
||||
const => 1
|
||||
);",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
inst: component lib.foo.bar
|
||||
port map (
|
||||
clk => clk_foo
|
||||
);",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
inst: component lib.foo.bar
|
||||
generic map (
|
||||
const => 1
|
||||
)
|
||||
port map (
|
||||
clk => clk_foo
|
||||
);",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_for_generate_statement() {
|
||||
check_statement(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
foo <= bar;
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
begin
|
||||
foo <= bar;
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
begin
|
||||
foo <= bar;
|
||||
end;
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
signal foo: natural;
|
||||
begin
|
||||
foo <= bar;
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
signal foo: natural;
|
||||
begin
|
||||
foo <= bar;
|
||||
end;
|
||||
end generate;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_if_generate_statement() {
|
||||
check_statement(
|
||||
"\
|
||||
gen: if cond = true generate
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: if cond = true generate
|
||||
begin
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: if cond = true generate
|
||||
elsif cond2 = true generate
|
||||
else generate
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: if cond = true generate
|
||||
variable v1: boolean;
|
||||
begin
|
||||
foo1(clk);
|
||||
elsif cond2 = true generate
|
||||
variable v2: boolean;
|
||||
begin
|
||||
foo2(clk);
|
||||
else generate
|
||||
variable v3: boolean;
|
||||
begin
|
||||
foo3(clk);
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen: if alt1: cond = true generate
|
||||
end alt1;
|
||||
elsif alt2: cond2 = true generate
|
||||
end alt2;
|
||||
else alt3: generate
|
||||
end alt3;
|
||||
end generate;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_conditional_assignment() {
|
||||
check_statement("foo(0) <= bar(1, 2) when cond = true;");
|
||||
check_statement("foo(0) <= bar(1, 2) when cond = true else expr2 when cond2;");
|
||||
check_statement("foo(0) <= bar(1, 2) when cond = true else expr2;");
|
||||
check_statement("foo(0) <= bar(1, 2) after 2 ns when cond;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_aggregate_assignments() {
|
||||
check_statement("(foo, 1 => bar) <= integer_vector'(1, 2);");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_selected_assignments() {
|
||||
check_statement(
|
||||
"\
|
||||
with x(0) + 1 select foo(0) <= bar(1, 2) when 0 | 1, def when others;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
with x(0) + 1 select foo(0) <= transport bar(1, 2) after 2 ns when 0 | 1, def when others;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_case_generate_statements() {
|
||||
check_statement(
|
||||
"\
|
||||
gen: case expr(0) + 2 generate
|
||||
when 1 | 2 =>
|
||||
sig <= value;
|
||||
when others =>
|
||||
foo(clk);
|
||||
end generate;",
|
||||
);
|
||||
check_statement(
|
||||
"\
|
||||
gen1: case expr(0) + 2 generate
|
||||
when alt1: 1 | 2 =>
|
||||
sig <= value;
|
||||
when alt2: others =>
|
||||
foo(clk);
|
||||
end generate gen1;",
|
||||
);
|
||||
}
|
||||
}
|
||||
378
vhdl_lang/src/formatting/configuration.rs
Normal file
378
vhdl_lang/src/formatting/configuration.rs
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::{
|
||||
BindingIndication, BlockConfiguration, ComponentSpecification, ConfigurationDeclaration,
|
||||
ConfigurationItem, ConfigurationSpecification, EntityAspect, VUnitBindingIndication,
|
||||
};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::syntax::Kind;
|
||||
use crate::{indented, HasTokenSpan, TokenAccess, TokenSpan, VHDLFormatter};
|
||||
use vhdl_lang::ast::{ComponentConfiguration, InstantiationList};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_configuration(
|
||||
&self,
|
||||
configuration: &ConfigurationDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_context_clause(&configuration.context_clause, buffer);
|
||||
if let Some(item) = configuration.context_clause.last() {
|
||||
self.line_break_preserve_whitespace(item.span().end_token, buffer);
|
||||
}
|
||||
// configuration cfg of entity_name is
|
||||
self.format_token_span(
|
||||
TokenSpan::new(
|
||||
configuration.span.start_token,
|
||||
configuration.span.start_token + 4,
|
||||
),
|
||||
buffer,
|
||||
);
|
||||
indented!(buffer, {
|
||||
self.format_declarations(&configuration.decl, buffer);
|
||||
self.format_v_unit_binding_indications(&configuration.vunit_bind_inds, buffer);
|
||||
buffer.line_break();
|
||||
self.format_block_configuration(&configuration.block_config, buffer);
|
||||
});
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(configuration.end_token, configuration.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(configuration.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_v_unit_binding_indications(
|
||||
&self,
|
||||
v_units: &[VUnitBindingIndication],
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
for v_unit_bind_ind in v_units {
|
||||
buffer.line_break();
|
||||
self.format_v_unit_indication(v_unit_bind_ind, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_block_configuration(&self, config: &BlockConfiguration, buffer: &mut Buffer) {
|
||||
if !config.use_clauses.is_empty() {
|
||||
unreachable!("Not implemented on AST side")
|
||||
}
|
||||
// for
|
||||
self.format_token_id(config.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(config.block_spec.as_ref(), buffer);
|
||||
indented!(buffer, {
|
||||
for item in &config.items {
|
||||
buffer.line_break();
|
||||
match item {
|
||||
ConfigurationItem::Block(block_configuration) => {
|
||||
self.format_block_configuration(block_configuration, buffer)
|
||||
}
|
||||
ConfigurationItem::Component(component_configuration) => {
|
||||
self.format_component_configuration(component_configuration, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
buffer.line_break();
|
||||
// end
|
||||
self.format_token_id(config.span.end_token - 2, buffer);
|
||||
buffer.push_whitespace();
|
||||
// for
|
||||
self.format_token_id(config.span.end_token - 1, buffer);
|
||||
// ;
|
||||
self.format_token_id(config.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_component_configuration(
|
||||
&self,
|
||||
config: &ComponentConfiguration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_component_specification(&config.spec, buffer);
|
||||
indented!(buffer, {
|
||||
if let Some(binding_indication) = &config.bind_ind {
|
||||
buffer.line_break();
|
||||
self.format_binding_indication(binding_indication, buffer)
|
||||
}
|
||||
self.format_v_unit_binding_indications(&config.vunit_bind_inds, buffer);
|
||||
if let Some(block_configuration) = &config.block_config {
|
||||
buffer.line_break();
|
||||
self.format_block_configuration(block_configuration, buffer);
|
||||
}
|
||||
});
|
||||
buffer.line_break();
|
||||
// end
|
||||
self.format_token_id(config.span.end_token - 2, buffer);
|
||||
buffer.push_whitespace();
|
||||
// for
|
||||
self.format_token_id(config.span.end_token - 1, buffer);
|
||||
// ;
|
||||
self.format_token_id(config.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_binding_indication(&self, indication: &BindingIndication, buffer: &mut Buffer) {
|
||||
// use
|
||||
self.format_token_id(indication.span.start_token, buffer);
|
||||
if let Some(aspect) = &indication.entity_aspect {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(indication.span.start_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
match aspect {
|
||||
EntityAspect::Entity(entity, architecture) => {
|
||||
self.format_name(entity.as_ref(), buffer);
|
||||
if let Some(arch) = architecture {
|
||||
self.format_token_id(arch.token - 1, buffer);
|
||||
self.format_token_id(arch.token, buffer);
|
||||
self.format_token_id(arch.token + 1, buffer);
|
||||
}
|
||||
}
|
||||
EntityAspect::Configuration(config) => {
|
||||
self.format_name(config.as_ref(), buffer);
|
||||
}
|
||||
EntityAspect::Open => {}
|
||||
}
|
||||
}
|
||||
if let Some(map_aspect) = &indication.generic_map {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_map_aspect(map_aspect, buffer);
|
||||
});
|
||||
}
|
||||
if let Some(map_aspect) = &indication.port_map {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_map_aspect(map_aspect, buffer);
|
||||
});
|
||||
}
|
||||
self.format_token_id(indication.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_configuration_specification(
|
||||
&self,
|
||||
configuration: &ConfigurationSpecification,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_component_specification(&configuration.spec, buffer);
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_binding_indication(&configuration.bind_ind, buffer);
|
||||
self.format_v_unit_binding_indications(&configuration.vunit_bind_inds, buffer);
|
||||
});
|
||||
if let Some(end_token) = configuration.end_token {
|
||||
buffer.line_break();
|
||||
self.format_token_id(end_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(end_token + 1, buffer);
|
||||
self.format_token_id(configuration.span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_component_specification(
|
||||
&self,
|
||||
spec: &ComponentSpecification,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// for
|
||||
self.format_token_id(spec.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
match &spec.instantiation_list {
|
||||
InstantiationList::Labels(labels) => self.format_ident_list(labels, buffer),
|
||||
InstantiationList::Others => self.format_token_id(spec.span.start_token + 1, buffer),
|
||||
InstantiationList::All => self.format_token_id(spec.span.start_token + 1, buffer),
|
||||
}
|
||||
// :
|
||||
self.format_token_id(spec.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(spec.component_name.as_ref(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_v_unit_indication(
|
||||
&self,
|
||||
v_unit_binding_indication: &VUnitBindingIndication,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// use
|
||||
self.format_token_id(v_unit_binding_indication.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
// v_unit
|
||||
self.format_token_id(v_unit_binding_indication.span.start_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
for v_unit in &v_unit_binding_indication.vunit_list {
|
||||
self.format_name(v_unit.as_ref(), buffer);
|
||||
if self
|
||||
.tokens
|
||||
.get_token(v_unit.span.end_token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Comma)
|
||||
{
|
||||
self.format_token_id(v_unit.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
self.format_token_id(v_unit_binding_indication.span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::analysis::tests::Code;
|
||||
use crate::formatting::test_utils::check_formatted;
|
||||
|
||||
fn check_design_unit_formatted(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::design_file,
|
||||
|formatter, file, buffer| {
|
||||
formatter.format_any_design_unit(&file.design_units[0].1, buffer, true)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_configuration() {
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
end for;
|
||||
end;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
use lib.foo.bar;
|
||||
use lib2.foo.bar;
|
||||
for rtl(0)
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
for name(0 to 3)
|
||||
end for;
|
||||
for other_name
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
for name(0 to 3)
|
||||
for name(7 to 8)
|
||||
end for;
|
||||
end for;
|
||||
for other_name
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
use lib.foo.bar;
|
||||
use vunit baz.foobar;
|
||||
for rtl(0)
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
for inst: lib.pkg.comp
|
||||
for arch
|
||||
end for;
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
for inst: lib.pkg.comp
|
||||
use entity work.bar;
|
||||
use vunit baz;
|
||||
for arch
|
||||
end for;
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
for inst: lib.pkg.comp
|
||||
use entity lib.use_name;
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for rtl(0)
|
||||
for inst: lib.pkg.comp
|
||||
end for;
|
||||
for inst1, inst2, inst3: lib2.pkg.comp
|
||||
end for;
|
||||
for all: lib3.pkg.comp
|
||||
end for;
|
||||
for others: lib4.pkg.comp
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_entity_aspect() {
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for foo
|
||||
for inst: lib.pkg.comp
|
||||
use entity lib.use_name;
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for foo
|
||||
for inst: lib.pkg.comp
|
||||
use entity lib.foo.name(arch);
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
configuration cfg of entity_name is
|
||||
for foo
|
||||
for inst: lib.pkg.comp
|
||||
use configuration lib.foo.name;
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg;",
|
||||
);
|
||||
}
|
||||
}
|
||||
167
vhdl_lang/src/formatting/constraint.rs
Normal file
167
vhdl_lang/src/formatting/constraint.rs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{DiscreteRange, SubtypeConstraint};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::syntax::Kind;
|
||||
use crate::TokenAccess;
|
||||
use vhdl_lang::ast::{ElementConstraint, Range, RangeConstraint};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_subtype_constraint(
|
||||
&self,
|
||||
constraint: &WithTokenSpan<SubtypeConstraint>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
match &constraint.item {
|
||||
SubtypeConstraint::Range(range) => {
|
||||
self.format_token_id(constraint.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_range(range, buffer)
|
||||
}
|
||||
SubtypeConstraint::Array(ranges, opt_constraint) => {
|
||||
self.format_token_id(constraint.span.start_token, buffer);
|
||||
if ranges.is_empty() {
|
||||
// open
|
||||
self.format_token_id(constraint.span.start_token + 1, buffer);
|
||||
}
|
||||
for range in ranges {
|
||||
self.format_discrete_range(&range.item, buffer);
|
||||
if self
|
||||
.tokens
|
||||
.get_token(range.span.end_token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Comma)
|
||||
{
|
||||
self.format_token_id(range.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
if let Some(constraint) = opt_constraint {
|
||||
self.format_token_id(constraint.span.start_token - 1, buffer);
|
||||
self.format_subtype_constraint(constraint, buffer);
|
||||
} else {
|
||||
self.format_token_id(constraint.span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
SubtypeConstraint::Record(records) => {
|
||||
self.format_token_id(constraint.span.start_token, buffer);
|
||||
for record in records {
|
||||
self.format_element_constraint(record, buffer);
|
||||
if self
|
||||
.tokens
|
||||
.get_token(record.constraint.span.end_token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Comma)
|
||||
{
|
||||
self.format_token_id(record.constraint.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
self.format_token_id(constraint.span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_element_constraint(&self, constraint: &ElementConstraint, buffer: &mut Buffer) {
|
||||
self.format_token_id(constraint.ident.token, buffer);
|
||||
self.format_subtype_constraint(&constraint.constraint, buffer);
|
||||
}
|
||||
|
||||
pub fn format_range_constraint(&self, constraint: &RangeConstraint, buffer: &mut Buffer) {
|
||||
self.format_expression(constraint.left_expr.as_ref().as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(constraint.direction_token(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(constraint.right_expr.as_ref().as_ref(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_range(&self, range: &Range, buffer: &mut Buffer) {
|
||||
match range {
|
||||
Range::Range(constraint) => self.format_range_constraint(constraint, buffer),
|
||||
Range::Attribute(attribute) => self.format_attribute_name(attribute, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_discrete_range(&self, range: &DiscreteRange, buffer: &mut Buffer) {
|
||||
match range {
|
||||
DiscreteRange::Discrete(name, range) => {
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
if let Some(range) = range {
|
||||
buffer.push_whitespace();
|
||||
// range
|
||||
self.format_token_id(name.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_range(range, buffer);
|
||||
}
|
||||
}
|
||||
DiscreteRange::Range(range) => self.format_range(range, buffer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::test_utils::check_formatted;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::syntax::test::Code;
|
||||
|
||||
fn check_range(input: &str) {
|
||||
let code = Code::new(input);
|
||||
let range = code.range();
|
||||
let tokens = code.tokenize();
|
||||
let formatter = VHDLFormatter::new(&tokens);
|
||||
let mut buffer = Buffer::new();
|
||||
formatter.format_range(&range, &mut buffer);
|
||||
assert_eq!(buffer.as_str(), input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_simple_range() {
|
||||
check_range("0 to 5");
|
||||
check_range("0 downto 5 - C_OFFSET");
|
||||
}
|
||||
|
||||
fn check_subtype_indications(inputs: &[&str]) {
|
||||
for input in inputs {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::subtype_indication,
|
||||
|formatter, subtype_indication, buffer| {
|
||||
formatter.format_subtype_indication(subtype_indication, buffer)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_range_subtype_constraint() {
|
||||
check_subtype_indications(&[
|
||||
"integer range 0 to 2 - 1",
|
||||
"integer range lib.foo.bar'range",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_array_subtype_constraint() {
|
||||
check_subtype_indications(&[
|
||||
"integer_vector(2 - 1 downto 0)",
|
||||
"integer_vector(lib.foo.bar)",
|
||||
"integer_vector(lib.pkg.bar'range)",
|
||||
"integer_vector(open)",
|
||||
"integer_vector(2 - 1 downto 0, 11 to 14)",
|
||||
"integer_vector(2 - 1 downto 0, 11 to 14)(foo to bar)",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_record_subtype_constraint() {
|
||||
check_subtype_indications(&["axi_m2s_t(tdata(2 - 1 downto 0), tuser(3 to 5))"]);
|
||||
}
|
||||
}
|
||||
51
vhdl_lang/src/formatting/context.rs
Normal file
51
vhdl_lang/src/formatting/context.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::{ContextClause, ContextDeclaration, ContextItem};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::{HasTokenSpan, TokenSpan};
|
||||
use vhdl_lang::indented;
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_context(&self, context: &ContextDeclaration, buffer: &mut Buffer) {
|
||||
// context <name> is
|
||||
self.format_token_span(
|
||||
TokenSpan::new(context.span.start_token, context.span.start_token + 2),
|
||||
buffer,
|
||||
);
|
||||
indented!(buffer, {
|
||||
if !context.items.is_empty() {
|
||||
buffer.line_break();
|
||||
}
|
||||
self.format_context_clause(&context.items, buffer);
|
||||
});
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(context.end_token, context.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(context.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_context_clause(&self, clause: &ContextClause, buffer: &mut Buffer) {
|
||||
for (i, item) in clause.iter().enumerate() {
|
||||
match item {
|
||||
ContextItem::Use(use_clause) => self.format_use_clause(use_clause, buffer),
|
||||
ContextItem::Library(library_clause) => {
|
||||
self.format_library_clause(library_clause, buffer)
|
||||
}
|
||||
ContextItem::Context(context_reference) => {
|
||||
self.format_context_reference(context_reference, buffer)
|
||||
}
|
||||
}
|
||||
if i < clause.len() - 1 {
|
||||
self.line_break_preserve_whitespace(item.span().end_token, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
865
vhdl_lang/src/formatting/declaration.rs
Normal file
865
vhdl_lang/src/formatting/declaration.rs
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::{
|
||||
ArrayIndex, ComponentDeclaration, ContextReference, ElementDeclaration, EntityName,
|
||||
FileDeclaration, ModeViewDeclaration, ObjectDeclaration, PackageInstantiation,
|
||||
ProtectedTypeDeclarativeItem, SubtypeIndication, TypeDeclaration, TypeDefinition,
|
||||
};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::syntax::Kind;
|
||||
use crate::{indented, HasTokenSpan, TokenAccess, TokenId, TokenSpan};
|
||||
use vhdl_lang::ast::token_range::WithTokenSpan;
|
||||
use vhdl_lang::ast::{
|
||||
AliasDeclaration, Attribute, AttributeDeclaration, AttributeSpecification, Declaration,
|
||||
LibraryClause, ObjectClass, PhysicalTypeDeclaration, ProtectedTypeBody,
|
||||
ProtectedTypeDeclaration, UseClause,
|
||||
};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub(crate) fn format_declarations(
|
||||
&self,
|
||||
declarations: &[WithTokenSpan<Declaration>],
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if declarations.is_empty() {
|
||||
return;
|
||||
}
|
||||
buffer.line_break();
|
||||
for (i, item) in declarations.iter().enumerate() {
|
||||
self.format_declaration(item, buffer);
|
||||
if i < declarations.len() - 1 {
|
||||
self.line_break_preserve_whitespace(item.get_end_token(), buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_declaration(
|
||||
&self,
|
||||
declaration: &WithTokenSpan<Declaration>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
use Declaration::*;
|
||||
match &declaration.item {
|
||||
Object(object_decl) => {
|
||||
self.format_object_declaration(object_decl, declaration.span, buffer)
|
||||
}
|
||||
File(file_decl) => self.format_file_declaration(file_decl, declaration.span, buffer),
|
||||
Type(type_decl) => self.format_type_declaration(type_decl, declaration.span, buffer),
|
||||
Component(component) => self.format_component_declaration(component, buffer),
|
||||
Attribute(attribute) => self.format_attribute(attribute, declaration.span, buffer),
|
||||
Alias(alias) => self.format_alias_declaration(alias, declaration.span, buffer),
|
||||
SubprogramDeclaration(subprogram_declaration) => {
|
||||
self.format_subprogram_declaration(subprogram_declaration, buffer)
|
||||
}
|
||||
SubprogramInstantiation(subprogram_instantiation) => {
|
||||
self.format_subprogram_instantiation(subprogram_instantiation, buffer)
|
||||
}
|
||||
SubprogramBody(subprogram_body) => self.format_subprogram_body(subprogram_body, buffer),
|
||||
Use(use_clause) => self.format_use_clause(use_clause, buffer),
|
||||
Package(package_instantiation) => {
|
||||
self.format_package_instance(package_instantiation, buffer)
|
||||
}
|
||||
Configuration(configuration) => {
|
||||
self.format_configuration_specification(configuration, buffer)
|
||||
}
|
||||
View(view_declaration) => self.format_view(view_declaration, declaration.span, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_component_declaration(
|
||||
&self,
|
||||
component: &ComponentDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_token_span(
|
||||
TokenSpan::new(
|
||||
component.span.start_token,
|
||||
component.is_token.unwrap_or(component.span.start_token + 1),
|
||||
),
|
||||
buffer,
|
||||
);
|
||||
if let Some(generic_clause) = &component.generic_list {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(generic_clause, buffer);
|
||||
});
|
||||
}
|
||||
if let Some(port_clause) = &component.port_list {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(port_clause, buffer);
|
||||
});
|
||||
}
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(component.end_token, component.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(component.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_object_declaration(
|
||||
&self,
|
||||
object_decl: &ObjectDeclaration,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
if object_decl.class == ObjectClass::SharedVariable {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(span.start_token + 1, buffer);
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
self.format_ident_list(&object_decl.idents, buffer);
|
||||
self.format_token_id(object_decl.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(&object_decl.subtype_indication, buffer);
|
||||
self.format_default_expression(object_decl.expression.as_ref(), buffer);
|
||||
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_file_declaration(
|
||||
&self,
|
||||
file_decl: &FileDeclaration,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_ident_list(&file_decl.idents, buffer);
|
||||
self.format_token_id(file_decl.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(&file_decl.subtype_indication, buffer);
|
||||
if let Some((token, open_information)) = &file_decl.open_info {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(*token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(open_information.as_ref(), buffer);
|
||||
}
|
||||
if let Some((token, file_name)) = &file_decl.file_name {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(*token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(file_name.as_ref(), buffer);
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_type_declaration(
|
||||
&self,
|
||||
type_decl: &TypeDeclaration,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, type_decl.ident.tree.token),
|
||||
buffer,
|
||||
);
|
||||
if let Some(is_token) = type_decl.is_token() {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(is_token, buffer);
|
||||
}
|
||||
if let Some(is_token) = type_decl.is_token() {
|
||||
buffer.push_whitespace();
|
||||
|
||||
self.format_type_definition(
|
||||
&type_decl.def,
|
||||
TokenSpan::new(
|
||||
is_token + 1,
|
||||
type_decl.end_ident_pos.unwrap_or(span.end_token) - 1,
|
||||
),
|
||||
buffer,
|
||||
);
|
||||
}
|
||||
if let Some(end_ident) = type_decl.end_ident_pos {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(end_ident, buffer);
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_type_definition(
|
||||
&self,
|
||||
definition: &TypeDefinition,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
use TypeDefinition::*;
|
||||
match definition {
|
||||
Enumeration(literals) => {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
for literal in literals {
|
||||
self.format_token_id(literal.tree.token, buffer);
|
||||
if self
|
||||
.tokens
|
||||
.get_token(literal.tree.token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Comma)
|
||||
{
|
||||
self.format_token_id(literal.tree.token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
Numeric(range) => {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_range(range, buffer)
|
||||
}
|
||||
Physical(physical_type) => {
|
||||
self.format_physical_type_declaration(physical_type, span, buffer)
|
||||
}
|
||||
Array(indices, of_token, subtype) => {
|
||||
self.format_array_type_declaration(indices, *of_token, subtype, span, buffer)
|
||||
}
|
||||
Record(elements) => self.format_record_declaration(elements, span, buffer),
|
||||
Access(subtype_indication) => {
|
||||
// access
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(subtype_indication, buffer);
|
||||
}
|
||||
Incomplete(_) => {
|
||||
// nothing to do
|
||||
}
|
||||
File(name) => {
|
||||
// file of
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, span.start_token + 1),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
}
|
||||
Protected(protected) => self.format_protected_type_declaration(protected, span, buffer),
|
||||
ProtectedBody(protected_body) => {
|
||||
self.format_protected_body_type_declaration(protected_body, span, buffer)
|
||||
}
|
||||
Subtype(subtype) => self.format_subtype_indication(subtype, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_physical_type_declaration(
|
||||
&self,
|
||||
declaration: &PhysicalTypeDeclaration,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// range
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_range(&declaration.range, buffer);
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_token_id(declaration.units_token, buffer);
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
// primary_unit;
|
||||
self.format_ident(&declaration.primary_unit, buffer);
|
||||
self.format_token_id(declaration.primary_unit.tree.token + 1, buffer);
|
||||
for (ident, literal) in &declaration.secondary_units {
|
||||
buffer.line_break();
|
||||
self.format_ident(ident, buffer);
|
||||
buffer.push_whitespace();
|
||||
// =
|
||||
self.format_token_id(ident.tree.token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_span(literal.span, buffer);
|
||||
// ;
|
||||
self.format_token_id(literal.span.end_token + 1, buffer);
|
||||
}
|
||||
});
|
||||
buffer.line_break();
|
||||
// end units
|
||||
self.format_token_span(TokenSpan::new(span.end_token - 1, span.end_token), buffer);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn format_array_type_declaration(
|
||||
&self,
|
||||
indices: &[ArrayIndex],
|
||||
of_token: TokenId,
|
||||
subtype: &SubtypeIndication,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// array
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
// (
|
||||
self.format_token_id(span.start_token + 1, buffer);
|
||||
for (i, index) in indices.iter().enumerate() {
|
||||
let end_token = match index {
|
||||
ArrayIndex::IndexSubtypeDefintion(name) => {
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(name.span.end_token + 1, name.span.end_token + 2),
|
||||
buffer,
|
||||
);
|
||||
name.span.end_token + 3
|
||||
}
|
||||
ArrayIndex::Discrete(discrete_range) => {
|
||||
self.format_discrete_range(&discrete_range.item, buffer);
|
||||
discrete_range.span.end_token + 1
|
||||
}
|
||||
};
|
||||
if i < indices.len() - 1 {
|
||||
self.format_token_id(end_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
// )
|
||||
self.format_token_id(of_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
// of
|
||||
self.format_token_id(of_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(subtype, buffer);
|
||||
}
|
||||
|
||||
pub fn format_record_declaration(
|
||||
&self,
|
||||
elements: &[ElementDeclaration],
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// record
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
let mut last_token = span.start_token;
|
||||
indented!(buffer, {
|
||||
for element in elements {
|
||||
buffer.line_break();
|
||||
self.format_element_declaration(element, buffer);
|
||||
last_token = element.span.end_token;
|
||||
}
|
||||
});
|
||||
buffer.line_break();
|
||||
// end record
|
||||
self.format_token_span(TokenSpan::new(last_token + 1, span.end_token), buffer)
|
||||
}
|
||||
|
||||
pub fn format_element_declaration(
|
||||
&self,
|
||||
declaration: &ElementDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_ident_list(&declaration.idents, buffer);
|
||||
// :
|
||||
self.format_token_id(declaration.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(&declaration.subtype, buffer);
|
||||
// ;
|
||||
self.format_token_id(declaration.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_protected_type_declaration(
|
||||
&self,
|
||||
declaration: &ProtectedTypeDeclaration,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// protected
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
let mut last_token = span.start_token;
|
||||
indented!(buffer, {
|
||||
for element in &declaration.items {
|
||||
buffer.line_break();
|
||||
match element {
|
||||
ProtectedTypeDeclarativeItem::Subprogram(subprogram) => {
|
||||
self.format_subprogram_declaration(subprogram, buffer);
|
||||
last_token = subprogram.span.end_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
buffer.line_break();
|
||||
// end protected
|
||||
self.format_token_span(TokenSpan::new(last_token + 1, span.end_token), buffer)
|
||||
}
|
||||
|
||||
pub fn format_protected_body_type_declaration(
|
||||
&self,
|
||||
declaration: &ProtectedTypeBody,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// protected body
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, span.start_token + 1),
|
||||
buffer,
|
||||
);
|
||||
indented!(buffer, {
|
||||
self.format_declarations(&declaration.decl, buffer)
|
||||
});
|
||||
buffer.line_break();
|
||||
let last_token = declaration
|
||||
.decl
|
||||
.last()
|
||||
.map(|last| last.span.end_token)
|
||||
.unwrap_or(span.start_token + 1);
|
||||
// end protected body
|
||||
self.format_token_span(TokenSpan::new(last_token + 1, span.end_token), buffer)
|
||||
}
|
||||
|
||||
pub fn format_attribute(&self, attribute: &Attribute, span: TokenSpan, buffer: &mut Buffer) {
|
||||
use Attribute::*;
|
||||
match attribute {
|
||||
Specification(spec) => self.format_attribute_specification(spec, span, buffer),
|
||||
Declaration(dec) => self.format_attribute_declaration(dec, span, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_attribute_declaration(
|
||||
&self,
|
||||
attribute: &AttributeDeclaration,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, attribute.ident.tree.token),
|
||||
buffer,
|
||||
);
|
||||
// :
|
||||
self.format_token_id(attribute.ident.tree.token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(attribute.type_mark.as_ref(), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_attribute_specification(
|
||||
&self,
|
||||
attribute: &AttributeSpecification,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// attribute <name> of
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, attribute.ident.item.token + 1),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
match &attribute.entity_name {
|
||||
EntityName::Name(name) => {
|
||||
self.format_token_id(name.designator.token, buffer);
|
||||
if let Some(signature) = &name.signature {
|
||||
self.format_signature(signature, buffer);
|
||||
}
|
||||
}
|
||||
EntityName::All | EntityName::Others => {
|
||||
self.format_token_id(attribute.ident.item.token + 2, buffer)
|
||||
}
|
||||
}
|
||||
// : <entity_class> is
|
||||
self.format_token_span(
|
||||
TokenSpan::new(attribute.colon_token, attribute.colon_token + 2),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(attribute.expr.as_ref(), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_alias_declaration(
|
||||
&self,
|
||||
alias: &AliasDeclaration,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// alias <name>
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, span.start_token + 1),
|
||||
buffer,
|
||||
);
|
||||
if let Some(subtype) = &alias.subtype_indication {
|
||||
// :
|
||||
self.format_token_id(span.start_token + 2, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(subtype, buffer);
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(alias.is_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(alias.name.as_ref(), buffer);
|
||||
if let Some(signature) = &alias.signature {
|
||||
self.format_signature(signature, buffer);
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_use_clause(&self, use_clause: &UseClause, buffer: &mut Buffer) {
|
||||
// use
|
||||
self.format_token_id(use_clause.get_start_token(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name_list(buffer, &use_clause.name_list);
|
||||
self.format_token_id(use_clause.get_end_token(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_library_clause(&self, library_clause: &LibraryClause, buffer: &mut Buffer) {
|
||||
// use
|
||||
self.format_token_id(library_clause.get_start_token(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_ident_list(&library_clause.name_list, buffer);
|
||||
self.format_token_id(library_clause.get_end_token(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_context_reference(
|
||||
&self,
|
||||
context_reference: &ContextReference,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// use
|
||||
self.format_token_id(context_reference.get_start_token(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name_list(buffer, &context_reference.name_list);
|
||||
self.format_token_id(context_reference.get_end_token(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_package_instance(&self, instance: &PackageInstantiation, buffer: &mut Buffer) {
|
||||
self.format_context_clause(&instance.context_clause, buffer);
|
||||
// package <name> is new
|
||||
self.format_token_span(
|
||||
TokenSpan::new(instance.get_start_token(), instance.get_start_token() + 3),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(instance.package_name.as_ref(), buffer);
|
||||
if let Some(generic_map) = &instance.generic_map {
|
||||
buffer.push_whitespace();
|
||||
self.format_map_aspect(generic_map, buffer);
|
||||
}
|
||||
self.format_token_id(instance.get_end_token(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_view(&self, view: &ModeViewDeclaration, span: TokenSpan, buffer: &mut Buffer) {
|
||||
// view <name> of
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, span.start_token + 2),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(&view.typ, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(view.is_token, buffer);
|
||||
indented!(buffer, {
|
||||
self.join_on_newline(&view.elements, Self::format_mode_view_element, buffer);
|
||||
});
|
||||
buffer.line_break();
|
||||
self.format_token_span(TokenSpan::new(view.end_token, span.end_token - 1), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::formatting::test_utils::{check_formatted, check_formatted_std};
|
||||
use crate::VHDLStandard;
|
||||
use crate::VHDLStandard::VHDL2019;
|
||||
|
||||
fn check_declaration(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
|code| code.declarative_part().into_iter().next().unwrap(),
|
||||
|formatter, ast, buffer| formatter.format_declaration(ast, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
fn check_declaration_std(input: &str, std: VHDLStandard) {
|
||||
check_formatted_std(
|
||||
input,
|
||||
input,
|
||||
std,
|
||||
|code| code.declarative_part().into_iter().next().unwrap(),
|
||||
|formatter, ast, buffer| formatter.format_declaration(ast, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_declarations() {
|
||||
check_declaration("constant my_const: std_logic;");
|
||||
check_declaration("variable my_var: std_logic;");
|
||||
check_declaration("signal foo: std_logic;");
|
||||
check_declaration("shared variable bar: std_logic;");
|
||||
|
||||
check_declaration("shared variable bar: std_logic := '0';");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_declarations() {
|
||||
check_declaration("file my_file: text;");
|
||||
check_declaration("file my_file: text is \"my_file.txt\";");
|
||||
check_declaration("file my_file: text open mode is \"my_file.txt\";");
|
||||
check_declaration("file FileID1, FileID2: text;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_declaration() {
|
||||
check_declaration("type my_enum is (A);");
|
||||
check_declaration("type my_enum is (A, B);");
|
||||
check_declaration("type my_enum is ('0', '1', 'U', 'X');");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric_type_declaration() {
|
||||
check_declaration("type my_enum is range 0 to 5;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn physical_types() {
|
||||
check_declaration(
|
||||
"\
|
||||
type TIME is range -9223372036854775807 to 9223372036854775807
|
||||
units
|
||||
fs; -- femtosecond
|
||||
end units;",
|
||||
);
|
||||
|
||||
check_declaration(
|
||||
"\
|
||||
type TIME is range -9223372036854775807 to 9223372036854775807
|
||||
units
|
||||
fs; -- femtosecond
|
||||
ps = 1000 fs; -- picosecond
|
||||
ns = 1000 ps; -- nanosecond
|
||||
us = 1000 ns; -- microsecond
|
||||
ms = 1000 us; -- millisecond
|
||||
sec = 1000 ms; -- second
|
||||
min = 60 sec; -- minute
|
||||
hr = 60 min; -- hour
|
||||
end units;",
|
||||
);
|
||||
|
||||
check_declaration(
|
||||
"\
|
||||
type TIME is range -9223372036854775807 to 9223372036854775807
|
||||
units
|
||||
fs; -- femtosecond
|
||||
ps = fs; -- picosecond
|
||||
end units;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_type_definition() {
|
||||
check_declaration("type my_array is array (natural range <>) of std_logic_vector;");
|
||||
check_declaration("type foo is array (2 - 1 downto 0, integer range <>) of boolean;");
|
||||
check_declaration("type foo is array (2 - 1 downto 0) of boolean;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_type_definition() {
|
||||
check_declaration(
|
||||
"\
|
||||
type x is record
|
||||
end record;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
type foo is record
|
||||
element: boolean;
|
||||
end record;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
type foo is record
|
||||
element: boolean;
|
||||
other_element: std_logic_vector;
|
||||
end foo;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
type dummy_rec is record
|
||||
dummy: bit;
|
||||
end record;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn access_definition() {
|
||||
check_declaration("type dummy_rec is access bit;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incomplete_type_definition() {
|
||||
check_declaration("type incomplete;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_definitions() {
|
||||
check_declaration("type foo is file of character;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protected_declaration() {
|
||||
check_declaration(
|
||||
"type foo is protected
|
||||
end protected;",
|
||||
);
|
||||
|
||||
check_declaration(
|
||||
"type foo is protected
|
||||
end protected foo;",
|
||||
);
|
||||
|
||||
check_declaration(
|
||||
"type foo is protected
|
||||
procedure proc;
|
||||
function fun return ret;
|
||||
end protected;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protected_body_declaration() {
|
||||
check_declaration(
|
||||
"type foo is protected body
|
||||
end protected body;",
|
||||
);
|
||||
|
||||
check_declaration(
|
||||
"\
|
||||
type foo is protected body
|
||||
variable foo: natural;
|
||||
procedure proc is
|
||||
begin
|
||||
end;
|
||||
end protected body;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protected_subtype_declaration() {
|
||||
check_declaration("subtype vec_t is integer_vector(2 - 1 downto 0);");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_declaration() {
|
||||
check_declaration(
|
||||
"\
|
||||
component foo
|
||||
end component;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
component foo is
|
||||
end component;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
component foo is
|
||||
end component foo;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
component foo is
|
||||
generic (
|
||||
foo: natural
|
||||
);
|
||||
end component;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
component foo is
|
||||
port (
|
||||
foo: natural
|
||||
);
|
||||
end component;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_attribute_declaration() {
|
||||
check_declaration("attribute foo: name;");
|
||||
check_declaration("attribute attr_name of foo: signal is 0 + 1;");
|
||||
check_declaration("attribute attr_name of \"**\": function is 0 + 1;");
|
||||
check_declaration("attribute attr_name of all: signal is 0 + 1;");
|
||||
check_declaration("attribute attr_name of foo[return natural]: function is 0 + 1;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_alias_declaration() {
|
||||
check_declaration("alias foo is name;");
|
||||
check_declaration("alias foo: vector(0 to 1) is name;");
|
||||
check_declaration("alias foo is name[return natural];");
|
||||
check_declaration("alias \"and\" is name;");
|
||||
check_declaration("alias 'c' is 'b';");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_use_clause() {
|
||||
check_declaration("use foo;");
|
||||
check_declaration("use foo, bar;");
|
||||
check_declaration("use foo, bar, baz;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_package_instance() {
|
||||
check_declaration("package ident is new foo;");
|
||||
check_declaration(
|
||||
"package ident is new foo generic map (
|
||||
foo => bar
|
||||
);",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_view() {
|
||||
check_declaration_std(
|
||||
"\
|
||||
view foo of bar is
|
||||
end view;",
|
||||
VHDL2019,
|
||||
);
|
||||
check_declaration_std(
|
||||
"\
|
||||
view foo of bar is
|
||||
end view foo;",
|
||||
VHDL2019,
|
||||
);
|
||||
check_declaration_std(
|
||||
"\
|
||||
view foo of bar is
|
||||
baz: in;
|
||||
end view;",
|
||||
VHDL2019,
|
||||
);
|
||||
check_declaration_std(
|
||||
"\
|
||||
view foo of bar is
|
||||
baz: in;
|
||||
bar, baz: view foo;
|
||||
end view;",
|
||||
VHDL2019,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_configuration_specification() {
|
||||
check_declaration(
|
||||
"\
|
||||
for all: lib.pkg.comp
|
||||
use entity work.foo(rtl);",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
for all: lib.pkg.comp
|
||||
use entity work.foo(rtl);
|
||||
end for;",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
for all: lib.pkg.comp
|
||||
use entity work.foo(rtl);
|
||||
use vunit bar, baz;
|
||||
end for;",
|
||||
);
|
||||
}
|
||||
}
|
||||
304
vhdl_lang/src/formatting/design.rs
Normal file
304
vhdl_lang/src/formatting/design.rs
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::{AnyDesignUnit, AnyPrimaryUnit, AnySecondaryUnit, PackageBody};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::HasTokenSpan;
|
||||
use vhdl_lang::ast::PackageDeclaration;
|
||||
use vhdl_lang::formatting::VHDLFormatter;
|
||||
use vhdl_lang::{indented, TokenSpan};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_any_design_unit(&self, unit: &AnyDesignUnit, buffer: &mut Buffer, is_last: bool) {
|
||||
use AnyDesignUnit::*;
|
||||
match unit {
|
||||
Primary(primary) => self.format_any_primary_unit(primary, buffer),
|
||||
Secondary(secondary) => self.format_any_secondary_unit(secondary, buffer),
|
||||
}
|
||||
if !is_last {
|
||||
buffer.line_breaks(2);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_any_primary_unit(&self, unit: &AnyPrimaryUnit, buffer: &mut Buffer) {
|
||||
use AnyPrimaryUnit::*;
|
||||
match unit {
|
||||
Entity(entity) => self.format_entity(entity, buffer),
|
||||
Configuration(configuration) => self.format_configuration(configuration, buffer),
|
||||
Package(package) => self.format_package(package, buffer),
|
||||
PackageInstance(package_instance) => {
|
||||
self.format_package_instance(package_instance, buffer)
|
||||
}
|
||||
Context(context) => self.format_context(context, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_any_secondary_unit(&self, unit: &AnySecondaryUnit, buffer: &mut Buffer) {
|
||||
use AnySecondaryUnit::*;
|
||||
match unit {
|
||||
Architecture(architecture) => self.format_architecture(architecture, buffer),
|
||||
PackageBody(body) => self.format_package_body(body, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_package(&self, package: &PackageDeclaration, buffer: &mut Buffer) {
|
||||
self.format_context_clause(&package.context_clause, buffer);
|
||||
if let Some(item) = package.context_clause.last() {
|
||||
self.line_break_preserve_whitespace(item.span().end_token, buffer);
|
||||
}
|
||||
// package <ident> is
|
||||
self.format_token_span(
|
||||
TokenSpan::new(package.span.start_token, package.span.start_token + 2),
|
||||
buffer,
|
||||
);
|
||||
indented!(buffer, {
|
||||
if let Some(generic_clause) = &package.generic_clause {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(generic_clause, buffer);
|
||||
}
|
||||
self.format_declarations(&package.decl, buffer);
|
||||
});
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(package.end_token, package.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(package.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_package_body(&self, body: &PackageBody, buffer: &mut Buffer) {
|
||||
self.format_context_clause(&body.context_clause, buffer);
|
||||
if let Some(item) = body.context_clause.last() {
|
||||
self.line_break_preserve_whitespace(item.span().end_token, buffer);
|
||||
}
|
||||
// package body <ident> is
|
||||
self.format_token_span(
|
||||
TokenSpan::new(body.span.start_token, body.span.start_token + 3),
|
||||
buffer,
|
||||
);
|
||||
indented!(buffer, { self.format_declarations(&body.decl, buffer) });
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(body.end_token, body.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(body.span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::analysis::tests::Code;
|
||||
use vhdl_lang::formatting::test_utils::check_formatted;
|
||||
|
||||
fn check_package_formatted(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::package_declaration,
|
||||
|formatter, package, buffer| formatter.format_package(package, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_simple_package() {
|
||||
check_package_formatted(
|
||||
"\
|
||||
package foo is
|
||||
end package;",
|
||||
);
|
||||
check_package_formatted(
|
||||
"\
|
||||
package foo is
|
||||
end package foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_package_with_declarations() {
|
||||
check_package_formatted(
|
||||
"\
|
||||
package pkg_name is
|
||||
type foo;
|
||||
constant bar: natural := 0;
|
||||
end package;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_package_with_generics() {
|
||||
check_package_formatted(
|
||||
"\
|
||||
package pkg_name is
|
||||
generic (
|
||||
type foo;
|
||||
type bar
|
||||
);
|
||||
end package;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_package_with_context_clause() {
|
||||
check_package_formatted(
|
||||
"\
|
||||
package pkg_name is
|
||||
generic (
|
||||
type foo;
|
||||
type bar
|
||||
);
|
||||
end package;",
|
||||
);
|
||||
}
|
||||
|
||||
fn check_context_formatted(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::context_declaration,
|
||||
|formatter, package, buffer| formatter.format_context(package, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_simple_context() {
|
||||
check_context_formatted(
|
||||
"\
|
||||
context ident is
|
||||
end;",
|
||||
);
|
||||
check_context_formatted(
|
||||
"\
|
||||
context ident is
|
||||
end context;",
|
||||
);
|
||||
check_context_formatted(
|
||||
"\
|
||||
context ident is
|
||||
end ident;",
|
||||
);
|
||||
check_context_formatted(
|
||||
"\
|
||||
context ident is
|
||||
end context ident;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_context_items() {
|
||||
check_context_formatted(
|
||||
"\
|
||||
context ident is
|
||||
library foo;
|
||||
use foo.bar;
|
||||
context foo.ctx;
|
||||
end context;",
|
||||
);
|
||||
}
|
||||
|
||||
fn check_design_unit_formatted(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::design_file,
|
||||
|formatter, file, buffer| {
|
||||
formatter.format_any_design_unit(&file.design_units[0].1, buffer, true)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn design_unit_context_clause_preserve_whitespaces() {
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
library lib;
|
||||
use lib.foo.all;
|
||||
|
||||
package pkg_name is
|
||||
end package;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
library lib;
|
||||
use lib.foo.all;
|
||||
package pkg_name is
|
||||
end package;",
|
||||
);
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
library lib;
|
||||
use lib.foo.all;
|
||||
|
||||
|
||||
|
||||
package pkg_name is
|
||||
end package;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_package_body() {
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
package body foo is
|
||||
end package body;",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_whitespace_preservation_context() {
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.std_logic_unsigned.all;
|
||||
use ieee.std_logic_arith.all;
|
||||
|
||||
library third_party;
|
||||
use third_party.baz;
|
||||
|
||||
use work.foo.bar;
|
||||
|
||||
package body foo is
|
||||
end package body;",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_whitespace_preservation_tokens_with_comments() {
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
library ieee;
|
||||
|
||||
-- This is a comment
|
||||
-- This is another comment
|
||||
-- Third comment
|
||||
library third_party;
|
||||
use third_party.baz;
|
||||
|
||||
package body foo is
|
||||
end package body;",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_whitespace_preservation_within_comments() {
|
||||
check_design_unit_formatted(
|
||||
"\
|
||||
-- This is a comment
|
||||
|
||||
|
||||
-- This ine appears later
|
||||
-- Third comment
|
||||
library third_party;
|
||||
use third_party.baz;
|
||||
|
||||
package body foo is
|
||||
end package body;",
|
||||
)
|
||||
}
|
||||
}
|
||||
187
vhdl_lang/src/formatting/entity.rs
Normal file
187
vhdl_lang/src/formatting/entity.rs
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::EntityDeclaration;
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::{HasTokenSpan, TokenSpan};
|
||||
use vhdl_lang::indented;
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_entity(&self, entity: &EntityDeclaration, buffer: &mut Buffer) {
|
||||
self.format_context_clause(&entity.context_clause, buffer);
|
||||
if let Some(item) = entity.context_clause.last() {
|
||||
self.line_break_preserve_whitespace(item.span().end_token, buffer);
|
||||
}
|
||||
let span = entity.span();
|
||||
// entity <ident> is
|
||||
self.format_token_span(TokenSpan::new(span.start_token, entity.is_token()), buffer);
|
||||
if let Some(generic_clause) = &entity.generic_clause {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(generic_clause, buffer);
|
||||
});
|
||||
}
|
||||
if let Some(port_clause) = &entity.port_clause {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(port_clause, buffer);
|
||||
});
|
||||
}
|
||||
|
||||
indented!(buffer, { self.format_declarations(&entity.decl, buffer) });
|
||||
if let Some(token) = entity.begin_token {
|
||||
buffer.line_break();
|
||||
self.format_token_id(token, buffer);
|
||||
}
|
||||
self.format_concurrent_statements(&entity.statements, buffer);
|
||||
buffer.line_break();
|
||||
// end [entity] [name];
|
||||
self.format_token_span(TokenSpan::new(entity.end_token, span.end_token - 1), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::analysis::tests::Code;
|
||||
use vhdl_lang::formatting::test_utils::check_formatted;
|
||||
|
||||
fn check_entity_formatted(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::entity_decl,
|
||||
|formatter, entity, buffer| formatter.format_entity(entity, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_simple_entity() {
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity my_ent is
|
||||
end entity my_ent;",
|
||||
);
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity my_ent is
|
||||
end my_ent;",
|
||||
);
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity my_ent is
|
||||
end;",
|
||||
);
|
||||
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity my_ent is
|
||||
end entity;",
|
||||
);
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity my_ent is
|
||||
begin
|
||||
end entity;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_with_comments() {
|
||||
check_entity_formatted(
|
||||
"\
|
||||
-- Some comment about the entity
|
||||
entity my_ent is
|
||||
end entity;",
|
||||
);
|
||||
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity my_ent is -- trailing comment
|
||||
end entity;",
|
||||
);
|
||||
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity /* Why would you put a comment here? */ my_ent is
|
||||
end entity;",
|
||||
);
|
||||
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity /* Why would you put a comment here? */ my_ent is -- this is an entity
|
||||
end entity;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_with_simple_generic() {
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity foo is
|
||||
-- Generics come here
|
||||
generic (
|
||||
foo: in std_logic --<This is it
|
||||
);
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_generic_default_value() {
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity foo is
|
||||
generic (
|
||||
foo: in std_logic := '1'
|
||||
);
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_with_ports() {
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity foo is
|
||||
port (
|
||||
foo: in std_logic := '1'
|
||||
);
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_with_generics_and_ports() {
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity foo is
|
||||
generic (
|
||||
a: in std_logic := '1'
|
||||
);
|
||||
port (
|
||||
B: in std_logic := '1'
|
||||
);
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_with_declarations() {
|
||||
check_entity_formatted(
|
||||
"\
|
||||
entity foo is
|
||||
port (
|
||||
foo: in std_logic := '1'
|
||||
);
|
||||
constant x: foo := bar;
|
||||
signal y: bar := foobar;
|
||||
end foo;",
|
||||
);
|
||||
}
|
||||
}
|
||||
286
vhdl_lang/src/formatting/expression.rs
Normal file
286
vhdl_lang/src/formatting/expression.rs
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{
|
||||
ElementAssociation, Expression, Operator, ResolutionIndication, SubtypeConstraint,
|
||||
SubtypeIndication,
|
||||
};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::HasTokenSpan;
|
||||
use vhdl_lang::ast::{Allocator, QualifiedExpression};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_expression(&self, expression: WithTokenSpan<&Expression>, buffer: &mut Buffer) {
|
||||
let span = expression.span;
|
||||
use Expression::*;
|
||||
match &expression.item {
|
||||
Binary(op, lhs, rhs) => {
|
||||
self.format_expression(lhs.as_ref().as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(op.token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(rhs.as_ref().as_ref(), buffer);
|
||||
}
|
||||
Unary(op, rhs) => {
|
||||
self.format_token_id(op.token, buffer);
|
||||
match op.item.item {
|
||||
Operator::Minus | Operator::Plus | Operator::QueQue => {
|
||||
// Leave as unary operator without whitespace
|
||||
}
|
||||
_ => buffer.push_whitespace(),
|
||||
}
|
||||
self.format_expression(rhs.as_ref().as_ref(), buffer);
|
||||
}
|
||||
Aggregate(aggregate) => {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
self.format_element_associations(aggregate, buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
Qualified(qualified_expr) => self.format_qualified_expression(qualified_expr, buffer),
|
||||
Name(name) => self.format_name(WithTokenSpan::new(name, span), buffer),
|
||||
Literal(_) => self.format_token_span(span, buffer),
|
||||
New(allocator) => self.format_allocator(allocator, buffer),
|
||||
Parenthesized(expression) => {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
self.format_expression(expression.as_ref().as_ref(), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_element_associations(
|
||||
&self,
|
||||
associations: &[WithTokenSpan<ElementAssociation>],
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
for (i, association) in associations.iter().enumerate() {
|
||||
match &association.item {
|
||||
ElementAssociation::Positional(expression) => {
|
||||
self.format_expression(expression.as_ref(), buffer)
|
||||
}
|
||||
ElementAssociation::Named(choices, expression) => {
|
||||
for (j, choice) in choices.iter().enumerate() {
|
||||
self.format_choice(choice, buffer);
|
||||
if j < choices.len() - 1 {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(choice.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(expression.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(expression.as_ref(), buffer);
|
||||
}
|
||||
}
|
||||
if i < associations.len() - 1 {
|
||||
self.format_token_id(association.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_subtype_indication(&self, indication: &SubtypeIndication, buffer: &mut Buffer) {
|
||||
if let Some(resolution) = &indication.resolution {
|
||||
self.format_resolution_indication(resolution, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_name(indication.type_mark.as_ref(), buffer);
|
||||
if let Some(constraint) = &indication.constraint {
|
||||
if matches!(constraint.item, SubtypeConstraint::Range(_)) {
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_subtype_constraint(constraint, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_resolution_indication(
|
||||
&self,
|
||||
indication: &ResolutionIndication,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
match &indication {
|
||||
ResolutionIndication::FunctionName(name) => self.format_name(name.as_ref(), buffer),
|
||||
ResolutionIndication::ArrayElement(element) => {
|
||||
self.format_token_id(element.span.start_token - 1, buffer);
|
||||
self.format_name(element.as_ref(), buffer);
|
||||
self.format_token_id(element.span.end_token + 1, buffer);
|
||||
}
|
||||
ResolutionIndication::Record(record) => {
|
||||
let span = record.span;
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
for (i, element_resolution) in record.item.iter().enumerate() {
|
||||
self.format_token_id(element_resolution.ident.token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_resolution_indication(&element_resolution.resolution, buffer);
|
||||
if i < record.item.len() - 1 {
|
||||
// ,
|
||||
self.format_token_id(
|
||||
element_resolution.resolution.get_end_token() + 1,
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to format ` := <expression>`
|
||||
pub(crate) fn format_default_expression(
|
||||
&self,
|
||||
expression: Option<&WithTokenSpan<Expression>>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if let Some(expr) = expression {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(expr.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(expr.as_ref(), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_qualified_expression(
|
||||
&self,
|
||||
expression: &QualifiedExpression,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_name(expression.type_mark.as_ref(), buffer);
|
||||
// '
|
||||
self.format_token_id(expression.type_mark.span.end_token + 1, buffer);
|
||||
self.format_expression(expression.expr.as_ref(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_allocator(&self, allocator: &WithTokenSpan<Allocator>, buffer: &mut Buffer) {
|
||||
// new
|
||||
self.format_token_id(allocator.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
match &allocator.item {
|
||||
Allocator::Qualified(expr) => self.format_qualified_expression(expr, buffer),
|
||||
Allocator::Subtype(subtype) => self.format_subtype_indication(subtype, buffer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::analysis::tests::Code;
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use vhdl_lang::formatting::buffer::Buffer;
|
||||
use vhdl_lang::formatting::test_utils::check_formatted;
|
||||
|
||||
fn check_expression(input: &str) {
|
||||
let code = Code::new(input);
|
||||
let expression = code.expr();
|
||||
let tokens = code.tokenize();
|
||||
let formatter = VHDLFormatter::new(&tokens);
|
||||
let mut buffer = Buffer::new();
|
||||
formatter.format_expression(
|
||||
WithTokenSpan::new(&expression.item, code.token_span()),
|
||||
&mut buffer,
|
||||
);
|
||||
assert_eq!(buffer.as_str(), input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_expression() {
|
||||
check_expression("name")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parenthesized_expression() {
|
||||
check_expression("(name)");
|
||||
check_expression("(A) or (B)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formal_literal() {
|
||||
check_expression("12387.44e7");
|
||||
check_expression("7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binary_expressions() {
|
||||
check_expression("1 + B");
|
||||
check_expression("2 sll 2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unary_expressions() {
|
||||
check_expression("+B");
|
||||
check_expression("-2");
|
||||
check_expression("not A")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complex_expression() {
|
||||
check_expression("A + B - C");
|
||||
check_expression("(A * B) + C");
|
||||
check_expression("((A * B) + C)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aggregate() {
|
||||
check_expression("(1, 2)");
|
||||
check_expression("(1 => 2, 3)");
|
||||
check_expression("(others => 1, others => 2)");
|
||||
check_expression("(1 downto 0 => 2)");
|
||||
check_expression("(0 to 1 => 2)");
|
||||
check_expression("(1 | 2 => 3)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qualified_expressions() {
|
||||
check_expression("integer_vector'(0, 1)");
|
||||
check_expression("foo'(1 + 2)");
|
||||
check_expression("foo'(others => '1')");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allocator_expressions() {
|
||||
check_expression("new integer_vector'(0, 1)");
|
||||
check_expression("new integer_vector");
|
||||
check_expression("new integer_vector(0 to 1)");
|
||||
check_expression("new integer_vector(foo'range)");
|
||||
}
|
||||
|
||||
fn check_subtype_indication(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::subtype_indication,
|
||||
|formatter, subtype_indication, buffer| {
|
||||
formatter.format_subtype_indication(subtype_indication, buffer)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolution_indication() {
|
||||
check_subtype_indication("resolve std_logic");
|
||||
check_subtype_indication("(resolve) integer_vector");
|
||||
check_subtype_indication("(elem resolve) rec_t");
|
||||
check_subtype_indication(
|
||||
"(elem1 (resolve1), elem2 resolve2, elem3 (sub_elem sub_resolve)) rec_t",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression_with_comments() {
|
||||
check_expression(
|
||||
"\
|
||||
-- Some comment
|
||||
A & -- And
|
||||
B & -- as well as
|
||||
C",
|
||||
)
|
||||
}
|
||||
}
|
||||
389
vhdl_lang/src/formatting/interface.rs
Normal file
389
vhdl_lang/src/formatting/interface.rs
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::{
|
||||
ActualPart, AssociationElement, ElementMode, InterfaceDeclaration, InterfaceList,
|
||||
InterfaceObjectDeclaration, InterfacePackageDeclaration, InterfacePackageGenericMapAspect,
|
||||
InterfaceSubprogramDeclaration, MapAspect, ModeIndication, ModeViewElement,
|
||||
ModeViewIndicationKind, SeparatedList, SimpleModeIndication, SubprogramDefault,
|
||||
};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::syntax::Kind;
|
||||
use crate::{indented, HasTokenSpan, TokenAccess};
|
||||
use vhdl_lang::ast::token_range::WithTokenSpan;
|
||||
use vhdl_lang::ast::{InterfaceFileDeclaration, ModeViewIndication};
|
||||
use vhdl_lang::TokenSpan;
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub(crate) fn format_interface_list(&self, clause: &InterfaceList, buffer: &mut Buffer) {
|
||||
let span = clause.span;
|
||||
let end_token = if self.tokens.index(span.start_token).kind == Kind::LeftPar {
|
||||
// We start with a `(` immediately
|
||||
// applicable for parameters (though VHDL 2008 allows an optional `parameter` keyword)
|
||||
span.start_token
|
||||
} else {
|
||||
// We start with a `generic`, `port` or `parameter` keyword
|
||||
span.start_token + 1
|
||||
};
|
||||
// port (
|
||||
// generic (
|
||||
// parameter (
|
||||
// (
|
||||
self.format_token_span(TokenSpan::new(span.start_token, end_token), buffer);
|
||||
indented!(buffer, {
|
||||
for (i, item) in clause.items.iter().enumerate() {
|
||||
buffer.line_break();
|
||||
self.format_interface_declaration(item, buffer);
|
||||
if i < clause.items.len() - 1 {
|
||||
self.format_token_id(item.get_end_token() + 1, buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
if !clause.items.is_empty() {
|
||||
buffer.line_break();
|
||||
}
|
||||
if self.tokens.index(span.end_token).kind == Kind::SemiColon {
|
||||
// );
|
||||
self.format_token_id(span.end_token - 1, buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
} else {
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_map_aspect_span(
|
||||
&self,
|
||||
list: &SeparatedList<AssociationElement>,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// port map (
|
||||
// generic map (
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.start_token, span.start_token + 2),
|
||||
buffer,
|
||||
);
|
||||
indented!(buffer, {
|
||||
for (i, item) in list.items.iter().enumerate() {
|
||||
buffer.line_break();
|
||||
self.format_association_element(item, buffer);
|
||||
if let Some(token) = list.tokens.get(i) {
|
||||
self.format_token_id(*token, buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
if !list.items.is_empty() {
|
||||
buffer.line_break();
|
||||
}
|
||||
// )
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_map_aspect(&self, aspect: &MapAspect, buffer: &mut Buffer) {
|
||||
self.format_map_aspect_span(&aspect.list, aspect.span, buffer);
|
||||
}
|
||||
|
||||
pub fn format_association_element(&self, element: &AssociationElement, buffer: &mut Buffer) {
|
||||
if let Some(formal) = &element.formal {
|
||||
self.format_name(formal.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(formal.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_actual_part(&element.actual, buffer)
|
||||
}
|
||||
|
||||
pub fn format_actual_part(&self, actual_part: &WithTokenSpan<ActualPart>, buffer: &mut Buffer) {
|
||||
match &actual_part.item {
|
||||
ActualPart::Expression(expression) => {
|
||||
self.format_expression(WithTokenSpan::new(expression, actual_part.span), buffer)
|
||||
}
|
||||
ActualPart::Open => self.format_token_span(actual_part.span, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_interface_declaration(
|
||||
&self,
|
||||
declaration: &InterfaceDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
use InterfaceDeclaration::*;
|
||||
match declaration {
|
||||
Object(object) => self.format_interface_object(object, buffer),
|
||||
File(file) => self.format_interface_file_declaration(file, buffer),
|
||||
Type(type_decl) => {
|
||||
self.format_token_id(type_decl.tree.token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_ident(type_decl, buffer);
|
||||
}
|
||||
Subprogram(subprogram) => {
|
||||
self.format_interface_subprogram_declaration(subprogram, buffer)
|
||||
}
|
||||
Package(package) => self.format_interface_package_declaration(package, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_interface_file_declaration(
|
||||
&self,
|
||||
declaration: &InterfaceFileDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_token_id(declaration.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_ident_list(&declaration.idents, buffer);
|
||||
self.format_token_id(declaration.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(&declaration.subtype_indication, buffer);
|
||||
}
|
||||
|
||||
pub fn format_interface_subprogram_declaration(
|
||||
&self,
|
||||
subprogram: &InterfaceSubprogramDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_subprogram_specification(&subprogram.specification, buffer);
|
||||
if let Some(default) = &subprogram.default {
|
||||
buffer.push_whitespace();
|
||||
// is
|
||||
self.format_token_id(subprogram.specification.span().end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
match default {
|
||||
SubprogramDefault::Name(name) => self.format_name(name.as_ref(), buffer),
|
||||
SubprogramDefault::Box => {
|
||||
self.format_token_id(subprogram.specification.span().end_token + 2, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_interface_package_declaration(
|
||||
&self,
|
||||
package: &InterfacePackageDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// package <ident> is new
|
||||
self.format_token_span(
|
||||
TokenSpan::new(package.span.start_token, package.span.start_token + 3),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(package.package_name.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
let span = package.generic_map.span();
|
||||
match &package.generic_map.item {
|
||||
InterfacePackageGenericMapAspect::Map(map) => {
|
||||
self.format_map_aspect_span(map, span, buffer)
|
||||
}
|
||||
InterfacePackageGenericMapAspect::Box | InterfacePackageGenericMapAspect::Default => {
|
||||
// generic
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
// map
|
||||
self.format_token_id(span.start_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
//(
|
||||
self.format_token_id(span.start_token + 2, buffer);
|
||||
// <> | default
|
||||
self.format_token_id(span.start_token + 3, buffer);
|
||||
// )
|
||||
self.format_token_id(span.start_token + 4, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_interface_object(
|
||||
&self,
|
||||
object: &InterfaceObjectDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// [signal] my_signal :
|
||||
self.format_token_span(
|
||||
TokenSpan::new(object.span.start_token, object.colon_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(object.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_mode(&object.mode, buffer);
|
||||
}
|
||||
|
||||
pub fn format_mode(&self, mode: &ModeIndication, buffer: &mut Buffer) {
|
||||
use ModeIndication::*;
|
||||
match mode {
|
||||
Simple(simple) => self.format_simple_mode(simple, buffer),
|
||||
View(mode) => self.format_mode_view_indication(mode, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_mode_view_indication(&self, mode: &ModeViewIndication, buffer: &mut Buffer) {
|
||||
// view
|
||||
self.format_token_id(mode.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
match &mode.kind {
|
||||
ModeViewIndicationKind::Array => {
|
||||
self.format_token_id(mode.name.span.start_token - 1, buffer);
|
||||
self.format_name(mode.name.as_ref(), buffer);
|
||||
self.format_token_id(mode.name.span.end_token + 1, buffer);
|
||||
}
|
||||
ModeViewIndicationKind::Record => {
|
||||
self.format_name(mode.name.as_ref(), buffer);
|
||||
}
|
||||
}
|
||||
if let Some((token, subtype)) = &mode.subtype_indication {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(*token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(subtype, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_element_mode(&self, mode: &ElementMode, buffer: &mut Buffer) {
|
||||
match mode {
|
||||
ElementMode::Simple(simple) => self.format_token_id(simple.token, buffer),
|
||||
ElementMode::Record(name) => {
|
||||
// view
|
||||
self.format_token_id(name.get_start_token() - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(name.as_ref(), buffer)
|
||||
}
|
||||
ElementMode::Array(name) => {
|
||||
//view (
|
||||
self.format_token_span(
|
||||
TokenSpan::new(name.get_start_token() - 2, name.get_start_token() - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
// )
|
||||
self.format_token_id(name.get_end_token() + 1, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_mode_view_element(&self, mode: &ModeViewElement, buffer: &mut Buffer) {
|
||||
self.format_ident_list(&mode.names, buffer);
|
||||
self.format_token_id(mode.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_element_mode(&mode.mode, buffer);
|
||||
// ;
|
||||
self.format_token_id(mode.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_simple_mode(&self, mode: &SimpleModeIndication, buffer: &mut Buffer) {
|
||||
if let Some(mode) = &mode.mode {
|
||||
self.format_token_id(mode.token, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_subtype_indication(&mode.subtype_indication, buffer);
|
||||
self.format_default_expression(mode.expression.as_ref(), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::analysis::tests::Code;
|
||||
use crate::formatting::test_utils::{check_formatted, check_formatted_std};
|
||||
use crate::VHDLStandard::VHDL2019;
|
||||
|
||||
fn check_generic(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::generic,
|
||||
|formatter, interface, buffer| {
|
||||
formatter.format_interface_declaration(interface, buffer)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_simple_object() {
|
||||
check_generic("my_generic: natural");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_simple_object_with_default() {
|
||||
check_generic("my_generic: natural := 7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_simple_object_with_explicit_mode() {
|
||||
check_generic("my_generic: in natural");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_object_with_class() {
|
||||
check_generic("constant my_generic: in natural");
|
||||
check_generic("constant my_generic: natural");
|
||||
}
|
||||
|
||||
fn check_element_mode(input: &str) {
|
||||
check_formatted_std(
|
||||
input,
|
||||
input,
|
||||
VHDL2019,
|
||||
Code::element_mode,
|
||||
|formatter, mode, buffer| formatter.format_element_mode(mode, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_element_mode() {
|
||||
check_element_mode("in");
|
||||
check_element_mode("out");
|
||||
check_element_mode("inout");
|
||||
check_element_mode("buffer");
|
||||
check_element_mode("linkage");
|
||||
check_element_mode("view foo");
|
||||
check_element_mode("view (foo)");
|
||||
}
|
||||
|
||||
fn check_port(input: &str) {
|
||||
check_formatted_std(
|
||||
input,
|
||||
input,
|
||||
VHDL2019,
|
||||
Code::port,
|
||||
|formatter, interface, buffer| {
|
||||
formatter.format_interface_declaration(interface, buffer)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_mode_view_indication() {
|
||||
check_port("signal foo: view bar");
|
||||
check_port("signal foo: view (bar)");
|
||||
check_port("signal foo: view bar of baz");
|
||||
check_port("signal foo: view (bar) of baz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_interface_file_declaration() {
|
||||
check_port("file valid: text");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_interface_subprogram_declaration() {
|
||||
check_generic("function foo return bar");
|
||||
check_generic("procedure foo");
|
||||
check_generic("impure function foo return bar");
|
||||
check_generic("function foo return bar is lib.name");
|
||||
check_generic("function foo return bar is <>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_interface_package_declaration() {
|
||||
check_generic(
|
||||
"\
|
||||
package foo is new lib.pkg generic map (
|
||||
foo => bar
|
||||
)",
|
||||
);
|
||||
check_generic("package foo is new lib.pkg generic map (<>)");
|
||||
check_generic("package foo is new lib.pkg generic map (default)");
|
||||
}
|
||||
}
|
||||
122
vhdl_lang/src/formatting/mod.rs
Normal file
122
vhdl_lang/src/formatting/mod.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::DesignFile;
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::syntax::Kind;
|
||||
use crate::{Token, TokenAccess};
|
||||
use vhdl_lang::ast::HasIdent;
|
||||
|
||||
mod architecture;
|
||||
mod buffer;
|
||||
mod concurrent_statement;
|
||||
mod configuration;
|
||||
mod constraint;
|
||||
mod context;
|
||||
mod declaration;
|
||||
mod design;
|
||||
mod entity;
|
||||
mod expression;
|
||||
mod interface;
|
||||
mod name;
|
||||
mod sequential_statement;
|
||||
mod statement;
|
||||
mod subprogram;
|
||||
mod token;
|
||||
|
||||
/// The formatter is the main entry point used for formatting a single
|
||||
/// Design Unit from AST representation to string representation. In that sense,
|
||||
/// the Formatter is the inverse to the Parser.
|
||||
///
|
||||
/// Most methods herein are called `format_<node>` where `node` is the AST node to format.
|
||||
/// Rather than returning a string, the methods accept a mutable [Buffer] object that they
|
||||
/// use to format.
|
||||
///
|
||||
/// The formatter is capable of retaining comment information as well as preserving newlines.
|
||||
pub struct VHDLFormatter<'b> {
|
||||
tokens: &'b Vec<Token>,
|
||||
}
|
||||
|
||||
impl<'b> VHDLFormatter<'b> {
|
||||
pub fn new(tokens: &'b Vec<Token>) -> VHDLFormatter<'b> {
|
||||
VHDLFormatter { tokens }
|
||||
}
|
||||
|
||||
/// Format a whole design file.
|
||||
pub fn format_design_file(file: &DesignFile) -> String {
|
||||
let mut result = Buffer::new();
|
||||
for (i, (tokens, design_unit)) in file.design_units.iter().enumerate() {
|
||||
let formatter = VHDLFormatter::new(tokens);
|
||||
formatter.format_any_design_unit(
|
||||
design_unit,
|
||||
&mut result,
|
||||
i == file.design_units.len() - 1,
|
||||
);
|
||||
}
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_ident_list<T: HasIdent>(&self, idents: &[T], buffer: &mut Buffer) {
|
||||
for ident in idents {
|
||||
let token = ident.ident().token;
|
||||
self.format_token_id(token, buffer);
|
||||
if self
|
||||
.tokens
|
||||
.get_token(token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Comma)
|
||||
{
|
||||
self.format_token_id(token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// indents the provided block and de-indents at the end.
|
||||
#[macro_export]
|
||||
macro_rules! indented {
|
||||
($buffer:ident, $block:block) => {
|
||||
$buffer.increase_indent();
|
||||
$block
|
||||
$buffer.decrease_indent();
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_utils {
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::syntax::test::Code;
|
||||
use vhdl_lang::VHDLStandard;
|
||||
|
||||
pub(crate) fn check_formatted<T>(
|
||||
input: &str,
|
||||
expected: &str,
|
||||
to_ast: impl FnOnce(&Code) -> T,
|
||||
format: impl FnOnce(&VHDLFormatter, &T, &mut Buffer),
|
||||
) {
|
||||
check_formatted_std(input, expected, VHDLStandard::default(), to_ast, format)
|
||||
}
|
||||
|
||||
pub(crate) fn check_formatted_std<T>(
|
||||
input: &str,
|
||||
expected: &str,
|
||||
std: VHDLStandard,
|
||||
to_ast: impl FnOnce(&Code) -> T,
|
||||
format: impl FnOnce(&VHDLFormatter, &T, &mut Buffer),
|
||||
) {
|
||||
let code = Code::with_standard(input, std);
|
||||
let ast_element = to_ast(&code);
|
||||
let tokens = code.tokenize();
|
||||
let formatter = VHDLFormatter::new(&tokens);
|
||||
let mut buffer = Buffer::new();
|
||||
format(&formatter, &ast_element, &mut buffer);
|
||||
assert_eq!(buffer.as_str(), expected);
|
||||
}
|
||||
}
|
||||
192
vhdl_lang/src/formatting/name.rs
Normal file
192
vhdl_lang/src/formatting/name.rs
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{CallOrIndexed, ExternalName, ExternalPath};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::syntax::Kind;
|
||||
use crate::{TokenAccess, TokenSpan};
|
||||
use vhdl_lang::ast::{AttributeName, Name};
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_name(&self, name: WithTokenSpan<&Name>, buffer: &mut Buffer) {
|
||||
use Name::*;
|
||||
let span = name.span;
|
||||
match &name.item {
|
||||
Designator(_) => self.join_token_span(span, buffer),
|
||||
Selected(name, designator) => {
|
||||
self.format_name(name.as_ref().as_ref(), buffer);
|
||||
self.join_token_span(
|
||||
TokenSpan::new(designator.token - 1, designator.token),
|
||||
buffer,
|
||||
);
|
||||
}
|
||||
SelectedAll(name) => {
|
||||
self.format_name(name.as_ref().as_ref(), buffer);
|
||||
self.join_token_span(TokenSpan::new(span.end_token - 1, span.end_token), buffer);
|
||||
}
|
||||
Slice(name, range) => {
|
||||
self.format_name(name.as_ref().as_ref(), buffer);
|
||||
self.format_token_id(name.span.end_token + 1, buffer);
|
||||
self.format_discrete_range(range, buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
Attribute(attr_name) => self.format_attribute_name(attr_name, buffer),
|
||||
CallOrIndexed(call_or_indexed) => {
|
||||
self.format_call_or_indexed(call_or_indexed, span, buffer)
|
||||
}
|
||||
External(external) => {
|
||||
self.format_external_name(WithTokenSpan::new(external, span), buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_call_or_indexed(
|
||||
&self,
|
||||
call: &CallOrIndexed,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_name(call.name.as_ref(), buffer);
|
||||
let open_paren = call.name.span.end_token + 1;
|
||||
if self.tokens.index(open_paren).kind == Kind::LeftPar {
|
||||
self.format_token_id(open_paren, buffer);
|
||||
}
|
||||
for (i, parameter) in call.parameters.items.iter().enumerate() {
|
||||
self.format_association_element(parameter, buffer);
|
||||
if let Some(token) = call.parameters.tokens.get(i) {
|
||||
self.format_token_id(*token, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
let close_paren = span.end_token;
|
||||
if self.tokens.index(close_paren).kind == Kind::RightPar {
|
||||
self.format_token_id(close_paren, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_attribute_name(&self, name: &AttributeName, buffer: &mut Buffer) {
|
||||
self.format_name(name.name.as_ref(), buffer);
|
||||
if let Some(signature) = &name.signature {
|
||||
self.format_signature(signature, buffer);
|
||||
}
|
||||
// '
|
||||
self.format_token_id(name.attr.token - 1, buffer);
|
||||
self.format_token_id(name.attr.token, buffer);
|
||||
if let Some(expr) = &name.expr {
|
||||
self.format_token_id(expr.span.start_token - 1, buffer);
|
||||
self.format_expression(expr.as_ref().as_ref(), buffer);
|
||||
self.format_token_id(expr.span.end_token + 1, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_external_name(&self, name: WithTokenSpan<&ExternalName>, buffer: &mut Buffer) {
|
||||
// <<
|
||||
self.format_token_id(name.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
// entity class
|
||||
self.format_token_id(name.span.start_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
let path = &name.item.path;
|
||||
match &path.item {
|
||||
ExternalPath::Package(name) => {
|
||||
// @
|
||||
self.format_token_id(name.span.start_token - 1, buffer);
|
||||
self.format_name(name.as_ref(), buffer)
|
||||
}
|
||||
ExternalPath::Absolute(name) => {
|
||||
// .
|
||||
self.format_token_id(name.span.start_token - 1, buffer);
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
}
|
||||
ExternalPath::Relative(name, up_levels) => {
|
||||
for i in (1..=*up_levels).rev() {
|
||||
// ^
|
||||
self.format_token_id(name.span.start_token - (2 * i), buffer);
|
||||
// .
|
||||
self.format_token_id(name.span.start_token - (2 * i - 1), buffer);
|
||||
}
|
||||
self.format_name(name.as_ref(), buffer)
|
||||
}
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(name.item.colon_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_subtype_indication(&name.item.subtype, buffer);
|
||||
buffer.push_whitespace();
|
||||
// >>
|
||||
self.format_token_id(name.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_name_list(&self, buffer: &mut Buffer, names: &[WithTokenSpan<Name>]) {
|
||||
for name in names {
|
||||
self.format_name(name.as_ref(), buffer);
|
||||
if self
|
||||
.tokens
|
||||
.get_token(name.span.end_token + 1)
|
||||
.is_some_and(|token| token.kind == Kind::Comma)
|
||||
{
|
||||
self.format_token_id(name.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::syntax::test::Code;
|
||||
use vhdl_lang::formatting::test_utils::check_formatted;
|
||||
|
||||
pub fn check_name(input: &str) {
|
||||
check_formatted(input, input, Code::name, |formatter, ast, buffer| {
|
||||
formatter.format_name(ast.as_ref(), buffer)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_names() {
|
||||
check_name("\"+\"");
|
||||
check_name("\"AND\"");
|
||||
check_name("\"and\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selected_names() {
|
||||
check_name("foo.bar.baz");
|
||||
check_name("foo.all");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_names() {
|
||||
check_name("prefix(0 to 3)");
|
||||
check_name("prefix(3 downto 0)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attribute_name() {
|
||||
check_name("prefix'subtype");
|
||||
check_name("prefix'element");
|
||||
check_name("prefix'foo(expr + 1)");
|
||||
check_name("prefix[return natural]'foo(expr + 1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complex_names() {
|
||||
check_name("prefix(foo(0)'range)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_names() {
|
||||
check_name("<< signal dut.gen(0) : std_logic >>");
|
||||
check_name("<< signal .dut.gen(0) : std_logic >>");
|
||||
check_name("<< signal @dut.gen(0) : std_logic >>");
|
||||
check_name("<< signal ^.dut.gen(0) : std_logic >>");
|
||||
check_name("<< signal ^.^.^.dut.gen(0) : std_logic >>");
|
||||
}
|
||||
}
|
||||
671
vhdl_lang/src/formatting/sequential_statement.rs
Normal file
671
vhdl_lang/src/formatting/sequential_statement.rs
Normal file
|
|
@ -0,0 +1,671 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{
|
||||
AssignmentRightHand, CaseStatement, Choice, DelayMechanism, Expression, Ident, IterationScheme,
|
||||
LabeledSequentialStatement, LoopStatement, ReportStatement, SequentialStatement,
|
||||
SignalAssignment, WaitStatement, WithRef,
|
||||
};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::{HasTokenSpan, TokenSpan};
|
||||
use vhdl_lang::ast::{
|
||||
ExitStatement, IfStatement, NextStatement, SignalForceAssignment, SignalReleaseAssignment,
|
||||
VariableAssignment,
|
||||
};
|
||||
use vhdl_lang::formatting::VHDLFormatter;
|
||||
use vhdl_lang::indented;
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_sequential_statements(
|
||||
&self,
|
||||
statements: &[LabeledSequentialStatement],
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if statements.is_empty() {
|
||||
return;
|
||||
}
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
for (i, item) in statements.iter().enumerate() {
|
||||
self.format_labeled_sequential_statement(item, buffer);
|
||||
if i < statements.len() - 1 {
|
||||
self.line_break_preserve_whitespace(item.statement.get_end_token(), buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn format_labeled_sequential_statement(
|
||||
&self,
|
||||
statement: &LabeledSequentialStatement,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_optional_label(statement.label.tree.as_ref(), buffer);
|
||||
self.format_sequential_statement(&statement.statement, buffer);
|
||||
}
|
||||
|
||||
pub fn format_sequential_statement(
|
||||
&self,
|
||||
statement: &WithTokenSpan<SequentialStatement>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
use SequentialStatement::*;
|
||||
let span = statement.span;
|
||||
match &statement.item {
|
||||
Wait(wait_statement) => self.format_wait_statement(wait_statement, span, buffer),
|
||||
Assert(assert) => {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_assert_statement(assert, buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
Report(report) => self.format_report_statement(report, span, buffer),
|
||||
VariableAssignment(variable_assignment) => {
|
||||
self.format_variable_assignment(variable_assignment, span, buffer)
|
||||
}
|
||||
SignalAssignment(signal_assignment) => {
|
||||
self.format_signal_assignment(signal_assignment, span, buffer)
|
||||
}
|
||||
SignalForceAssignment(signal_assignment) => {
|
||||
self.format_signal_force_assignment(signal_assignment, span, buffer)
|
||||
}
|
||||
SignalReleaseAssignment(signal_assignment) => {
|
||||
self.format_signal_release_assignment(signal_assignment, span, buffer)
|
||||
}
|
||||
ProcedureCall(call_or_indexed) => {
|
||||
self.format_call_or_indexed(&call_or_indexed.item, call_or_indexed.span, buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
If(if_statement) => {
|
||||
self.format_if_statement(if_statement, span, buffer);
|
||||
}
|
||||
Case(case_statement) => {
|
||||
self.format_case_statement(case_statement, span, buffer);
|
||||
}
|
||||
Loop(loop_statement) => {
|
||||
self.format_loop_statement(loop_statement, span, buffer);
|
||||
}
|
||||
Next(next_statement) => self.format_next_statement(next_statement, span, buffer),
|
||||
Exit(exit_statement) => self.format_exit_statement(exit_statement, span, buffer),
|
||||
|
||||
Return(stmt) => {
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
if let Some(expr) = &stmt.expression {
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(expr.as_ref(), buffer);
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
Null => self.join_token_span(span, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_wait_statement(
|
||||
&self,
|
||||
statement: &WaitStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// wait
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
if let Some(name_list) = &statement.sensitivity_clause {
|
||||
buffer.push_whitespace();
|
||||
// on
|
||||
self.format_token_id(span.start_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name_list(buffer, name_list);
|
||||
}
|
||||
if let Some(condition_clause) = &statement.condition_clause {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(condition_clause.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(condition_clause.as_ref(), buffer);
|
||||
}
|
||||
if let Some(timeout_clause) = &statement.timeout_clause {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(timeout_clause.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(timeout_clause.as_ref(), buffer);
|
||||
}
|
||||
|
||||
// ;
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_report_statement(
|
||||
&self,
|
||||
report: &ReportStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// report
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(report.report.as_ref(), buffer);
|
||||
self.format_opt_severity(report.severity.as_ref(), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_variable_assignment(
|
||||
&self,
|
||||
assignment: &VariableAssignment,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if let AssignmentRightHand::Selected(selected) = &assignment.rhs {
|
||||
// with
|
||||
self.format_token_id(selected.expression.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(selected.expression.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
// select
|
||||
self.format_token_id(selected.expression.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_target(&assignment.target, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(assignment.target.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_assignment_right_hand(
|
||||
&assignment.rhs,
|
||||
|formatter, expr, buffer| formatter.format_expression(expr.as_ref(), buffer),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_signal_assignment(
|
||||
&self,
|
||||
assignment: &SignalAssignment,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_target(&assignment.target, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(assignment.target.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
if let Some(delay_mechanism) = &assignment.delay_mechanism {
|
||||
self.format_delay_mechanism(delay_mechanism, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_assignment_right_hand(&assignment.rhs, Self::format_waveform, buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_delay_mechanism(
|
||||
&self,
|
||||
delay_mechanism: &WithTokenSpan<DelayMechanism>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
match &delay_mechanism.item {
|
||||
DelayMechanism::Transport => {
|
||||
self.format_token_span(delay_mechanism.span, buffer);
|
||||
}
|
||||
DelayMechanism::Inertial { reject } => {
|
||||
if let Some(reject) = reject {
|
||||
self.format_token_id(delay_mechanism.span.start_token, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(reject.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
// inertial
|
||||
self.format_token_id(delay_mechanism.span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_signal_force_assignment(
|
||||
&self,
|
||||
assignment: &SignalForceAssignment,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_target(&assignment.target, buffer);
|
||||
buffer.push_whitespace();
|
||||
// <=
|
||||
self.format_token_id(assignment.target.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
// force
|
||||
self.format_token_id(assignment.target.span.end_token + 2, buffer);
|
||||
buffer.push_whitespace();
|
||||
if assignment.force_mode.is_some() {
|
||||
self.format_token_id(assignment.target.span.end_token + 3, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_assignment_right_hand(
|
||||
&assignment.rhs,
|
||||
|formatter, expr, buffer| formatter.format_expression(expr.as_ref(), buffer),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_signal_release_assignment(
|
||||
&self,
|
||||
assignment: &SignalReleaseAssignment,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_target(&assignment.target, buffer);
|
||||
buffer.push_whitespace();
|
||||
// <=
|
||||
self.format_token_id(assignment.target.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
// release
|
||||
self.format_token_id(assignment.target.span.end_token + 2, buffer);
|
||||
if assignment.force_mode.is_some() {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(assignment.target.span.end_token + 3, buffer);
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_if_statement(
|
||||
&self,
|
||||
statement: &IfStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
for cond in &statement.conds.conditionals {
|
||||
let condition = &cond.condition;
|
||||
// if | elsif
|
||||
self.format_token_id(condition.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(condition.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
// then
|
||||
self.format_token_id(condition.span.end_token + 1, buffer);
|
||||
self.format_sequential_statements(&cond.item, buffer);
|
||||
buffer.line_break();
|
||||
}
|
||||
if let Some((statements, token)) = &statement.conds.else_item {
|
||||
self.format_token_id(*token, buffer);
|
||||
self.format_sequential_statements(statements, buffer);
|
||||
buffer.line_break();
|
||||
}
|
||||
if statement.end_label_pos.is_some() {
|
||||
// end if <label>
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.end_token - 3, span.end_token - 1),
|
||||
buffer,
|
||||
)
|
||||
} else {
|
||||
// end if
|
||||
self.format_token_span(
|
||||
TokenSpan::new(span.end_token - 2, span.end_token - 1),
|
||||
buffer,
|
||||
)
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_choice(&self, choice: &WithTokenSpan<Choice>, buffer: &mut Buffer) {
|
||||
match &choice.item {
|
||||
Choice::Expression(expr) => {
|
||||
self.format_expression(WithTokenSpan::new(expr, choice.span), buffer)
|
||||
}
|
||||
Choice::DiscreteRange(range) => self.format_discrete_range(range, buffer),
|
||||
Choice::Others => self.format_token_span(choice.span, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_case_statement(
|
||||
&self,
|
||||
statement: &CaseStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// case
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
if statement.is_matching {
|
||||
// ?
|
||||
self.format_token_id(span.start_token + 1, buffer);
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(statement.expression.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
// is
|
||||
self.format_token_id(statement.expression.span.end_token + 1, buffer);
|
||||
indented!(buffer, {
|
||||
for alternative in &statement.alternatives {
|
||||
buffer.line_break();
|
||||
for (i, choice) in alternative.choices.iter().enumerate() {
|
||||
if i == 0 {
|
||||
// when
|
||||
self.format_token_id(choice.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
self.format_choice(choice, buffer);
|
||||
if i < alternative.choices.len() - 1 {
|
||||
buffer.push_whitespace();
|
||||
// |
|
||||
self.format_token_id(choice.span.end_token + 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
if i == alternative.choices.len() - 1 {
|
||||
buffer.push_whitespace();
|
||||
// =>
|
||||
self.format_token_id(choice.span.end_token + 1, buffer);
|
||||
}
|
||||
}
|
||||
self.format_sequential_statements(&alternative.item, buffer);
|
||||
}
|
||||
});
|
||||
buffer.line_break();
|
||||
if statement.is_matching {
|
||||
self.format_token_span(
|
||||
TokenSpan::new(statement.end_token, span.end_token - 2),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(span.end_token - 1, buffer);
|
||||
} else {
|
||||
self.format_token_span(
|
||||
TokenSpan::new(statement.end_token, span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
}
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_loop_statement(
|
||||
&self,
|
||||
statement: &LoopStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if let Some(scheme) = &statement.iteration_scheme {
|
||||
match scheme {
|
||||
IterationScheme::While(expression) => {
|
||||
// while
|
||||
self.format_token_id(expression.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(expression.as_ref(), buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
IterationScheme::For(ident, range) => {
|
||||
// for <ident> in
|
||||
self.format_token_span(
|
||||
TokenSpan::new(ident.tree.token - 1, ident.tree.token + 1),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
self.format_discrete_range(range, buffer);
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.format_token_id(statement.loop_token, buffer);
|
||||
self.format_sequential_statements(&statement.statements, buffer);
|
||||
buffer.line_break();
|
||||
self.format_token_span(
|
||||
TokenSpan::new(statement.end_token, span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_next_statement(
|
||||
&self,
|
||||
statement: &NextStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// next
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
self.format_opt_loop_label(statement.loop_label.as_ref(), buffer);
|
||||
self.format_opt_condition(statement.condition.as_ref(), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_exit_statement(
|
||||
&self,
|
||||
statement: &ExitStatement,
|
||||
span: TokenSpan,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// next
|
||||
self.format_token_id(span.start_token, buffer);
|
||||
self.format_opt_loop_label(statement.loop_label.as_ref(), buffer);
|
||||
self.format_opt_condition(statement.condition.as_ref(), buffer);
|
||||
self.format_token_id(span.end_token, buffer);
|
||||
}
|
||||
|
||||
fn format_opt_loop_label(&self, loop_label: Option<&WithRef<Ident>>, buffer: &mut Buffer) {
|
||||
if let Some(label) = loop_label {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(label.item.token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
fn format_opt_condition(
|
||||
&self,
|
||||
condition: Option<&WithTokenSpan<Expression>>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if let Some(condition) = &condition {
|
||||
buffer.push_whitespace();
|
||||
// when
|
||||
self.format_token_id(condition.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(condition.as_ref(), buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::formatting::test_utils::check_formatted;
|
||||
use crate::syntax::test::Code;
|
||||
|
||||
fn check_statement(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
Code::sequential_statement,
|
||||
|formatter, ast, buffer| formatter.format_labeled_sequential_statement(ast, buffer),
|
||||
)
|
||||
}
|
||||
|
||||
fn check_statements(inputs: &[&str]) {
|
||||
for input in inputs {
|
||||
check_statement(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_statement() {
|
||||
check_statement("null;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_statement() {
|
||||
check_statement("return;");
|
||||
check_statement("return 2 + 2;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calls() {
|
||||
check_statement("something;");
|
||||
check_statement("something(arg);");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assertions() {
|
||||
check_statement("assert x;");
|
||||
check_statement("assert x report y;");
|
||||
check_statement("assert x report y severity NOTE;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wait_statement() {
|
||||
check_statement("wait;");
|
||||
check_statement("foo: wait;");
|
||||
check_statement("wait on foo, bar;");
|
||||
check_statement("wait until a = b;");
|
||||
check_statement("wait for 2 ns;");
|
||||
check_statement("wait on foo until bar for 2 ns;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn report_statement() {
|
||||
check_statements(&["report \"message\" severity error;", "report \"message\";"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_assignment() {
|
||||
check_statements(&["foo(0) := bar(1, 2);", "name: foo(0) := bar(1, 2);"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signal_assignment() {
|
||||
check_statements(&["foo(0) <= bar(1, 2);", "name: foo(0) <= bar(1, 2);"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signal_force_assignment() {
|
||||
check_statements(&[
|
||||
"foo(0) <= force bar(1, 2);",
|
||||
"foo(0) <= force out bar(1, 2);",
|
||||
"foo(0) <= force in bar(1, 2);",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signal_release_assignment() {
|
||||
check_statements(&[
|
||||
"foo(0) <= release;",
|
||||
"foo(0) <= release out;",
|
||||
"foo(0) <= release in;",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_statements() {
|
||||
check_statements(&[
|
||||
"\
|
||||
if cond = true then
|
||||
foo(1, 2);
|
||||
x := 1;
|
||||
end if;",
|
||||
"\
|
||||
mylabel: if cond = true then
|
||||
foo(1, 2);
|
||||
x := 1;
|
||||
end if mylabel;",
|
||||
"\
|
||||
if cond = true then
|
||||
foo(1, 2);
|
||||
else
|
||||
x := 1;
|
||||
end if;",
|
||||
"\
|
||||
mylabel: if cond = true then
|
||||
foo(1, 2);
|
||||
else
|
||||
x := 1;
|
||||
end if mylabel;",
|
||||
"\
|
||||
if cond = true then
|
||||
foo(1, 2);
|
||||
elsif cond2 = false then
|
||||
y := 2;
|
||||
else
|
||||
x := 1;
|
||||
end if;",
|
||||
"\
|
||||
mylabel: if cond = true then
|
||||
foo(1, 2);
|
||||
elsif cond2 = false then
|
||||
y := 2;
|
||||
else
|
||||
x := 1;
|
||||
end if mylabel;",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_statements() {
|
||||
check_statements(&[
|
||||
"\
|
||||
case foo(1) is
|
||||
when 1 | 2 =>
|
||||
stmt1;
|
||||
stmt2;
|
||||
when others =>
|
||||
stmt3;
|
||||
stmt4;
|
||||
end case;",
|
||||
"\
|
||||
case? foo(1) is
|
||||
when others =>
|
||||
null;
|
||||
end case?;",
|
||||
])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_loop() {
|
||||
check_statements(&[
|
||||
"\
|
||||
lbl: loop
|
||||
end loop lbl;",
|
||||
"\
|
||||
lbl: loop
|
||||
stmt1;
|
||||
stmt2;
|
||||
end loop lbl;",
|
||||
"\
|
||||
while foo = true loop
|
||||
stmt1;
|
||||
stmt2;
|
||||
end loop;",
|
||||
"\
|
||||
for idx in 0 to 3 loop
|
||||
stmt1;
|
||||
stmt2;
|
||||
end loop;",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_next_statement() {
|
||||
check_statements(&[
|
||||
"next;",
|
||||
"next foo;",
|
||||
"next when condition;",
|
||||
"next foo when condition;",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_exit_statement() {
|
||||
check_statements(&[
|
||||
"exit;",
|
||||
"exit foo;",
|
||||
"exit when condition;",
|
||||
"exit foo when condition;",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_delay_mechanisms() {
|
||||
check_statements(&[
|
||||
"foo(0) <= transport bar(1, 2);",
|
||||
"bar <= reject 2 ns inertial bar(1, 2);",
|
||||
"bar <= inertial bar(1, 2);",
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_selected_assignments() {
|
||||
check_statement(
|
||||
"\
|
||||
with x(0) + 1 select foo(0) := bar(1, 2) when 0 | 1, def when others;",
|
||||
);
|
||||
}
|
||||
}
|
||||
26
vhdl_lang/src/formatting/statement.rs
Normal file
26
vhdl_lang/src/formatting/statement.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::Expression;
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::VHDLFormatter;
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub(crate) fn format_opt_severity(
|
||||
&self,
|
||||
severity: Option<&WithTokenSpan<Expression>>,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
if let Some(severity) = &severity {
|
||||
buffer.push_whitespace();
|
||||
self.format_token_id(severity.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_expression(severity.as_ref(), buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
318
vhdl_lang/src/formatting/subprogram.rs
Normal file
318
vhdl_lang/src/formatting/subprogram.rs
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{
|
||||
Signature, SubprogramDeclaration, SubprogramHeader, SubprogramInstantiation,
|
||||
SubprogramSpecification,
|
||||
};
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::{HasTokenSpan, TokenSpan};
|
||||
use vhdl_lang::ast::{FunctionSpecification, ProcedureSpecification, SubprogramBody};
|
||||
use vhdl_lang::indented;
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub fn format_subprogram_declaration(
|
||||
&self,
|
||||
declaration: &SubprogramDeclaration,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
self.format_subprogram_specification(&declaration.specification, buffer);
|
||||
// ;
|
||||
self.format_token_id(declaration.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_subprogram_specification(
|
||||
&self,
|
||||
specification: &SubprogramSpecification,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
use SubprogramSpecification::*;
|
||||
match specification {
|
||||
Procedure(procedure_spec) => {
|
||||
self.format_procedure_specification(procedure_spec, buffer)
|
||||
}
|
||||
Function(function_spec) => self.format_function_specification(function_spec, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_procedure_specification(
|
||||
&self,
|
||||
specification: &ProcedureSpecification,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// procedure <name>
|
||||
self.format_token_span(
|
||||
TokenSpan::new(
|
||||
specification.span.start_token,
|
||||
specification.designator.tree.token,
|
||||
),
|
||||
buffer,
|
||||
);
|
||||
if let Some(header) = &specification.header {
|
||||
self.format_subprogram_header(header, buffer);
|
||||
}
|
||||
if let Some(parameter) = &specification.parameter_list {
|
||||
if specification.header.is_some() {
|
||||
buffer.increase_indent();
|
||||
buffer.line_break();
|
||||
}
|
||||
self.format_interface_list(parameter, buffer);
|
||||
if specification.header.is_some() {
|
||||
buffer.decrease_indent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_function_specification(
|
||||
&self,
|
||||
specification: &FunctionSpecification,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// function <name>
|
||||
self.format_token_span(
|
||||
TokenSpan::new(
|
||||
specification.span.start_token,
|
||||
specification.designator.tree.token,
|
||||
),
|
||||
buffer,
|
||||
);
|
||||
if let Some(header) = &specification.header {
|
||||
self.format_subprogram_header(header, buffer);
|
||||
}
|
||||
if let Some(parameter) = &specification.parameter_list {
|
||||
self.format_interface_list(parameter, buffer);
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
// return
|
||||
self.format_token_id(specification.return_type.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(specification.return_type.as_ref(), buffer);
|
||||
}
|
||||
|
||||
pub fn format_subprogram_header(&self, header: &SubprogramHeader, buffer: &mut Buffer) {
|
||||
indented!(buffer, {
|
||||
buffer.line_break();
|
||||
self.format_interface_list(&header.generic_list, buffer);
|
||||
});
|
||||
if let Some(map_aspect) = &header.map_aspect {
|
||||
buffer.push_whitespace();
|
||||
self.format_map_aspect(map_aspect, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_subprogram_body(&self, body: &SubprogramBody, buffer: &mut Buffer) {
|
||||
self.format_subprogram_specification(&body.specification, buffer);
|
||||
buffer.push_whitespace();
|
||||
// is
|
||||
self.format_token_id(body.specification.span().end_token + 1, buffer);
|
||||
buffer.line_break();
|
||||
indented!(buffer, {
|
||||
self.format_declarations(&body.declarations, buffer);
|
||||
});
|
||||
self.format_token_id(body.begin_token, buffer);
|
||||
self.format_sequential_statements(&body.statements, buffer);
|
||||
buffer.line_break();
|
||||
// end
|
||||
self.format_token_span(
|
||||
TokenSpan::new(body.end_token, body.span.end_token - 1),
|
||||
buffer,
|
||||
);
|
||||
// ;
|
||||
self.format_token_id(body.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_signature(&self, signature: &WithTokenSpan<Signature>, buffer: &mut Buffer) {
|
||||
self.format_token_id(signature.span.start_token, buffer);
|
||||
match &signature.item {
|
||||
Signature::Function(functions, return_type) => {
|
||||
for (i, function) in functions.iter().enumerate() {
|
||||
self.format_name(function.as_ref(), buffer);
|
||||
if i < functions.len() - 1 {
|
||||
// ,
|
||||
self.format_token_id(function.span.end_token + 1, buffer);
|
||||
}
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
// return
|
||||
self.format_token_id(return_type.span.start_token - 1, buffer);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(return_type.as_ref(), buffer);
|
||||
}
|
||||
Signature::Procedure(procedures) => {
|
||||
self.format_name_list(buffer, procedures);
|
||||
}
|
||||
}
|
||||
self.format_token_id(signature.span.end_token, buffer);
|
||||
}
|
||||
|
||||
pub fn format_subprogram_instantiation(
|
||||
&self,
|
||||
instantiation: &SubprogramInstantiation,
|
||||
buffer: &mut Buffer,
|
||||
) {
|
||||
// function <name> is new
|
||||
self.format_token_span(
|
||||
TokenSpan::new(
|
||||
instantiation.span.start_token,
|
||||
instantiation.span.start_token + 3,
|
||||
),
|
||||
buffer,
|
||||
);
|
||||
buffer.push_whitespace();
|
||||
self.format_name(instantiation.subprogram_name.as_ref(), buffer);
|
||||
if let Some(signature) = &instantiation.signature {
|
||||
self.format_signature(signature, buffer);
|
||||
}
|
||||
if let Some(generic_map) = &instantiation.generic_map {
|
||||
buffer.push_whitespace();
|
||||
self.format_map_aspect(generic_map, buffer);
|
||||
}
|
||||
self.format_token_id(instantiation.span.end_token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::formatting::test_utils::check_formatted;
|
||||
|
||||
fn check_signature(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
|code| code.signature(),
|
||||
|formatter, ast, buffer| formatter.format_signature(ast, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature() {
|
||||
check_signature("[return type_mark]");
|
||||
check_signature("[foo return bar]");
|
||||
check_signature("[type_mark]");
|
||||
check_signature("[foo, foo2 return bar]");
|
||||
}
|
||||
|
||||
fn check_subprogram_declaration(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
|code| code.subprogram_decl(),
|
||||
|formatter, ast, buffer| formatter.format_subprogram_declaration(ast, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subprogram_declaration_without_parameters() {
|
||||
check_subprogram_declaration("procedure foo;");
|
||||
check_subprogram_declaration("function foo return natural;");
|
||||
check_subprogram_declaration("function \"+\" return natural;");
|
||||
check_subprogram_declaration("impure function foo return natural;");
|
||||
check_subprogram_declaration("pure function foo return natural;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subprogram_declaration_one_parameter() {
|
||||
check_subprogram_declaration(
|
||||
"\
|
||||
procedure foo(
|
||||
a: std_logic
|
||||
);",
|
||||
);
|
||||
check_subprogram_declaration(
|
||||
"\
|
||||
function foo(
|
||||
a: std_logic
|
||||
) return std_logic;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subprogram_declaration_multiple_parameters() {
|
||||
check_subprogram_declaration(
|
||||
"\
|
||||
procedure foo(
|
||||
arg0: std_logic;
|
||||
arg1: std_logic
|
||||
);",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subprogram_declaration_with_generics() {
|
||||
check_subprogram_declaration(
|
||||
"\
|
||||
procedure foo
|
||||
generic (
|
||||
x: natural
|
||||
);",
|
||||
);
|
||||
check_subprogram_declaration(
|
||||
"\
|
||||
procedure foo
|
||||
generic (
|
||||
x: natural
|
||||
)
|
||||
parameter (
|
||||
a: std_logic
|
||||
);",
|
||||
);
|
||||
check_subprogram_declaration(
|
||||
"\
|
||||
procedure foo
|
||||
generic (
|
||||
x: natural
|
||||
)
|
||||
(
|
||||
a: std_logic
|
||||
);",
|
||||
);
|
||||
}
|
||||
|
||||
fn check_declaration(input: &str) {
|
||||
check_formatted(
|
||||
input,
|
||||
input,
|
||||
|code| code.declarative_part().into_iter().next().unwrap(),
|
||||
|formatter, ast, buffer| formatter.format_declaration(ast, buffer),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subprogram_body() {
|
||||
check_declaration(
|
||||
"\
|
||||
function \"+\"(
|
||||
arg: natural
|
||||
) return natural is
|
||||
begin
|
||||
end function \"+\";",
|
||||
);
|
||||
check_declaration(
|
||||
"\
|
||||
function foo(
|
||||
arg: natural
|
||||
) return natural is
|
||||
begin
|
||||
end function foo;",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subprogram_instantiation() {
|
||||
check_declaration("procedure my_proc is new proc;");
|
||||
check_declaration("function my_func is new func;");
|
||||
check_declaration("function my_func is new func[bit return bit_vector];");
|
||||
check_declaration(
|
||||
"\
|
||||
function my_func is new func[bit return bit_vector] generic map (
|
||||
x => x
|
||||
);",
|
||||
);
|
||||
}
|
||||
}
|
||||
50
vhdl_lang/src/formatting/token.rs
Normal file
50
vhdl_lang/src/formatting/token.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// Lic// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::WithDecl;
|
||||
use crate::formatting::buffer::Buffer;
|
||||
use crate::formatting::VHDLFormatter;
|
||||
use crate::{TokenAccess, TokenId};
|
||||
use std::cmp::max;
|
||||
use vhdl_lang::ast::Ident;
|
||||
use vhdl_lang::TokenSpan;
|
||||
|
||||
impl VHDLFormatter<'_> {
|
||||
pub(crate) fn format_token_id(&self, id: TokenId, buffer: &mut Buffer) {
|
||||
buffer.push_token(self.tokens.index(id));
|
||||
}
|
||||
|
||||
pub(crate) fn format_token_span(&self, span: TokenSpan, buffer: &mut Buffer) {
|
||||
for (index, id) in span.iter().enumerate() {
|
||||
self.format_token_id(id, buffer);
|
||||
if index < span.len() - 1 {
|
||||
buffer.push_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn join_token_span(&self, span: TokenSpan, buffer: &mut Buffer) {
|
||||
for id in span.iter() {
|
||||
self.format_token_id(id, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn format_ident(&self, ident: &WithDecl<Ident>, buffer: &mut Buffer) {
|
||||
self.format_token_id(ident.tree.token, buffer)
|
||||
}
|
||||
|
||||
pub(crate) fn line_break_preserve_whitespace(&self, token_id: TokenId, buffer: &mut Buffer) {
|
||||
let current_line = self.tokens.get_pos(token_id).end().line;
|
||||
if let Some(token) = self.tokens.get_token(token_id + 1) {
|
||||
let next_line = token.full_range().start.line;
|
||||
let numbers_of_whitespaces = max(next_line - current_line, 1);
|
||||
buffer.line_breaks(numbers_of_whitespaces)
|
||||
} else {
|
||||
buffer.line_break();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ mod project;
|
|||
mod syntax;
|
||||
|
||||
mod completion;
|
||||
mod formatting;
|
||||
mod standard;
|
||||
|
||||
pub use crate::config::Config;
|
||||
|
|
@ -28,6 +29,7 @@ pub use crate::data::{
|
|||
Diagnostic, Latin1String, Message, MessageHandler, MessagePrinter, MessageType,
|
||||
NullDiagnostics, NullMessages, Position, Range, Severity, SeverityMap, Source, SrcPos,
|
||||
};
|
||||
pub use formatting::VHDLFormatter;
|
||||
|
||||
pub use crate::analysis::EntHierarchy;
|
||||
pub use crate::named_entity::{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,27 @@
|
|||
|
||||
use clap::Parser;
|
||||
use itertools::Itertools;
|
||||
use std::path::Path;
|
||||
use vhdl_lang::{Config, Diagnostic, MessagePrinter, Project, Severity, SeverityMap};
|
||||
use std::iter::zip;
|
||||
use std::path::{Path, PathBuf};
|
||||
use vhdl_lang::ast::DesignFile;
|
||||
use vhdl_lang::{
|
||||
Config, Diagnostic, MessagePrinter, Project, Severity, SeverityMap, Source, VHDLFormatter,
|
||||
VHDLParser, VHDLStandard,
|
||||
};
|
||||
|
||||
#[derive(Debug, clap::Args)]
|
||||
#[group(required = true, multiple = false)]
|
||||
pub struct Group {
|
||||
/// Config file in TOML format containing libraries and settings
|
||||
#[arg(short, long)]
|
||||
config: Option<String>,
|
||||
|
||||
/// Format the passed file and write the contents to stdout.
|
||||
///
|
||||
/// This is experimental and the formatting behavior will change in the future.
|
||||
#[arg(short, long)]
|
||||
format: Option<String>,
|
||||
}
|
||||
|
||||
/// Run vhdl analysis
|
||||
#[derive(Parser, Debug)]
|
||||
|
|
@ -22,23 +41,82 @@ struct Args {
|
|||
#[arg(short = 'l', long)]
|
||||
libraries: Option<String>,
|
||||
|
||||
/// Config file in TOML format containing libraries and settings
|
||||
#[arg(short, long)]
|
||||
config: String,
|
||||
#[clap(flatten)]
|
||||
group: Group,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
if let Some(config_path) = args.group.config {
|
||||
parse_and_analyze_project(config_path, args.num_threads, args.libraries);
|
||||
} else if let Some(format) = args.group.format {
|
||||
format_file(format);
|
||||
}
|
||||
}
|
||||
|
||||
fn format_file(format: String) {
|
||||
let path = PathBuf::from(format);
|
||||
let parser = VHDLParser::new(VHDLStandard::default());
|
||||
let mut diagnostics = Vec::new();
|
||||
let result = parser.parse_design_file(&path, &mut diagnostics);
|
||||
match result {
|
||||
Ok((_, design_file)) => {
|
||||
if !diagnostics.is_empty() {
|
||||
show_diagnostics(&diagnostics, &SeverityMap::default());
|
||||
std::process::exit(1);
|
||||
}
|
||||
let result = VHDLFormatter::format_design_file(&design_file);
|
||||
println!("{result}");
|
||||
check_formatted_file(&path, parser, design_file, &result);
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_formatted_file(path: &Path, parser: VHDLParser, design_file: DesignFile, result: &str) {
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
let new_file = parser.parse_design_source(&Source::inline(path, result), &mut diagnostics);
|
||||
if !diagnostics.is_empty() {
|
||||
println!("Formatting failed as it resulted in a syntactically incorrect file.");
|
||||
show_diagnostics(&diagnostics, &SeverityMap::default());
|
||||
std::process::exit(1);
|
||||
}
|
||||
for ((tokens_a, _), (tokens_b, _)) in zip(new_file.design_units, design_file.design_units) {
|
||||
for (a, b) in zip(tokens_a, tokens_b) {
|
||||
if !a.equal_format(&b) {
|
||||
println!("Token mismatch");
|
||||
println!("New Token={a:#?}");
|
||||
let contents = a.pos.source.contents();
|
||||
let a_line = contents.get_line(a.pos.range.start.line as usize).unwrap();
|
||||
println!(" {a_line}");
|
||||
println!("Old Token={b:#?}");
|
||||
let b_line = result.lines().nth(b.pos.range.start.line as usize).unwrap();
|
||||
println!(" {b_line}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_and_analyze_project(
|
||||
config_path: String,
|
||||
num_threads: Option<usize>,
|
||||
libraries: Option<String>,
|
||||
) {
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(args.num_threads.unwrap_or(0))
|
||||
.num_threads(num_threads.unwrap_or(0))
|
||||
.build_global()
|
||||
.unwrap();
|
||||
|
||||
let mut config = Config::default();
|
||||
let mut msg_printer = MessagePrinter::default();
|
||||
config.load_external_config(&mut msg_printer, args.libraries.clone());
|
||||
config.load_external_config(&mut msg_printer, libraries.clone());
|
||||
config.append(
|
||||
&Config::read_file_path(Path::new(&args.config)).expect("Failed to read config file"),
|
||||
&Config::read_file_path(Path::new(&config_path)).expect("Failed to read config file"),
|
||||
&mut msg_printer,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@
|
|||
use crate::ast::ExternalObjectClass;
|
||||
use crate::ast::{
|
||||
AliasDeclaration, AnyDesignUnit, AnyPrimaryUnit, AnySecondaryUnit, Attribute,
|
||||
AttributeDeclaration, AttributeSpecification, ComponentDeclaration, Designator,
|
||||
FileDeclaration, HasIdent, Ident, InterfacePackageDeclaration, ModeViewDeclaration,
|
||||
ObjectClass, PackageInstantiation, SubprogramBody, SubprogramInstantiation,
|
||||
SubprogramSpecification, TypeDeclaration, WithDecl,
|
||||
AttributeDeclaration, AttributeSpecification, ComponentDeclaration, Designator, HasIdent,
|
||||
Ident, InterfacePackageDeclaration, ModeViewDeclaration, ObjectClass, PackageInstantiation,
|
||||
SubprogramBody, SubprogramInstantiation, SubprogramSpecification, TypeDeclaration, WithDecl,
|
||||
};
|
||||
use crate::data::*;
|
||||
mod types;
|
||||
|
|
@ -715,12 +714,6 @@ impl HasEntityId for AliasDeclaration {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for FileDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for TypeDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl.get()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub fn parse_alias_declaration(
|
|||
}
|
||||
};
|
||||
|
||||
ctx.stream.expect_kind(Is)?;
|
||||
let is_token = ctx.stream.expect_kind(Is)?;
|
||||
let name = parse_name(ctx)?;
|
||||
|
||||
let signature = {
|
||||
|
|
@ -44,6 +44,7 @@ pub fn parse_alias_declaration(
|
|||
AliasDeclaration {
|
||||
designator,
|
||||
subtype_indication,
|
||||
is_token,
|
||||
name,
|
||||
signature,
|
||||
},
|
||||
|
|
@ -65,6 +66,7 @@ mod tests {
|
|||
AliasDeclaration {
|
||||
designator: code.s1("foo").decl_designator(),
|
||||
subtype_indication: None,
|
||||
is_token: code.s1("is").token(),
|
||||
name: code.s1("name").name(),
|
||||
signature: None
|
||||
},
|
||||
|
|
@ -82,6 +84,7 @@ mod tests {
|
|||
AliasDeclaration {
|
||||
designator: code.s1("foo").decl_designator(),
|
||||
subtype_indication: Some(code.s1("vector(0 to 1)").subtype_indication()),
|
||||
is_token: code.s1("is").token(),
|
||||
name: code.s1("name").name(),
|
||||
signature: None
|
||||
},
|
||||
|
|
@ -99,6 +102,7 @@ mod tests {
|
|||
AliasDeclaration {
|
||||
designator: code.s1("foo").decl_designator(),
|
||||
subtype_indication: None,
|
||||
is_token: code.s1("is").token(),
|
||||
name: code.s1("name").name(),
|
||||
signature: Some(code.s1("[return natural]").signature())
|
||||
},
|
||||
|
|
@ -119,6 +123,7 @@ mod tests {
|
|||
AliasDeclaration {
|
||||
designator,
|
||||
subtype_indication: None,
|
||||
is_token: code.s1("is").token(),
|
||||
name: code.s1("name").name(),
|
||||
signature: None
|
||||
},
|
||||
|
|
@ -139,6 +144,7 @@ mod tests {
|
|||
AliasDeclaration {
|
||||
designator,
|
||||
subtype_indication: None,
|
||||
is_token: code.s1("is").token(),
|
||||
name: code.s1("'b'").name(),
|
||||
signature: None
|
||||
},
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ pub fn parse_attribute(ctx: &mut ParsingContext<'_>) -> ParseResult<Vec<WithToke
|
|||
},
|
||||
Of => {
|
||||
let entity_names = parse_entity_name_list(ctx)?;
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let entity_class = parse_entity_class(ctx)?;
|
||||
ctx.stream.expect_kind(Is)?;
|
||||
let expr = parse_expression(ctx)?;
|
||||
|
|
@ -111,6 +111,7 @@ pub fn parse_attribute(ctx: &mut ParsingContext<'_>) -> ParseResult<Vec<WithToke
|
|||
entity_name,
|
||||
entity_class,
|
||||
expr: expr.clone(),
|
||||
colon_token
|
||||
}),
|
||||
TokenSpan::new(start_token, end_token)
|
||||
)
|
||||
|
|
@ -152,6 +153,7 @@ mod tests {
|
|||
designator: code.s1("foo").ref_designator(),
|
||||
signature: None
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
entity_class: EntityClass::Signal,
|
||||
expr: code.s1("0+1").expr()
|
||||
}),
|
||||
|
|
@ -172,6 +174,7 @@ mod tests {
|
|||
designator: code.s1("\"**\"").ref_designator(),
|
||||
signature: None
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
entity_class: EntityClass::Function,
|
||||
expr: code.s1("0+1").expr()
|
||||
}),
|
||||
|
|
@ -193,6 +196,7 @@ mod tests {
|
|||
designator: code.s1("foo").ref_designator(),
|
||||
signature: None
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
entity_class: EntityClass::Signal,
|
||||
expr: code.s1("0+1").expr()
|
||||
}),
|
||||
|
|
@ -205,6 +209,7 @@ mod tests {
|
|||
designator: code.s1("bar").ref_designator(),
|
||||
signature: None
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
entity_class: EntityClass::Signal,
|
||||
expr: code.s1("0+1").expr()
|
||||
}),
|
||||
|
|
@ -223,6 +228,7 @@ mod tests {
|
|||
Attribute::Specification(AttributeSpecification {
|
||||
ident: WithRef::new(code.s1("attr_name").ident()),
|
||||
entity_name: EntityName::All,
|
||||
colon_token: code.s1(":").token(),
|
||||
entity_class: EntityClass::Signal,
|
||||
expr: code.s1("0+1").expr()
|
||||
}),
|
||||
|
|
@ -240,6 +246,7 @@ mod tests {
|
|||
Attribute::Specification(AttributeSpecification {
|
||||
ident: WithRef::new(code.s1("attr_name").ident()),
|
||||
entity_name: EntityName::Others,
|
||||
colon_token: code.s1(":").token(),
|
||||
entity_class: EntityClass::Signal,
|
||||
expr: code.s1("0+1").expr()
|
||||
}),
|
||||
|
|
@ -260,6 +267,7 @@ mod tests {
|
|||
designator: code.s1("foo").ref_designator(),
|
||||
signature: Some(code.s1("[return natural]").signature())
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
entity_class: EntityClass::Function,
|
||||
expr: code.s1("0+1").expr()
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -89,31 +89,34 @@ pub fn parse_component_declaration(
|
|||
) -> ParseResult<ComponentDeclaration> {
|
||||
let start_token = ctx.stream.expect_kind(Component)?;
|
||||
let ident = WithDecl::new(ctx.stream.expect_ident()?);
|
||||
ctx.stream.pop_if_kind(Is);
|
||||
let is_token = ctx.stream.pop_if_kind(Is);
|
||||
|
||||
let generic_list = parse_optional_generic_list(ctx)?;
|
||||
let port_list = parse_optional_port_list(ctx)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
if ctx.standard < VHDL2019 {
|
||||
ctx.stream.expect_kind(Component)?;
|
||||
} else {
|
||||
ctx.stream.pop_if_kind(Component);
|
||||
}
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let semicolon_token = expect_semicolon_or_last(ctx);
|
||||
|
||||
Ok(ComponentDeclaration {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, semicolon_token),
|
||||
is_token,
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, &ident.tree, end_ident),
|
||||
ident,
|
||||
generic_list,
|
||||
port_list,
|
||||
end_token,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::syntax::test::Code;
|
||||
use crate::VHDLStandard::VHDL2019;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
|
@ -131,10 +134,12 @@ end component;
|
|||
component,
|
||||
ComponentDeclaration {
|
||||
span: code.token_span(),
|
||||
is_token: None,
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
generic_list: None,
|
||||
port_list: None,
|
||||
end_ident_pos: None,
|
||||
end_token: code.s1("end").token(),
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -149,10 +154,12 @@ end component;
|
|||
component,
|
||||
ComponentDeclaration {
|
||||
span: code.token_span(),
|
||||
is_token: Some(code.s1("is").token()),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
generic_list: None,
|
||||
port_list: None,
|
||||
end_ident_pos: None,
|
||||
end_token: code.s1("end").token(),
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -167,10 +174,12 @@ end component foo;
|
|||
component,
|
||||
ComponentDeclaration {
|
||||
span: code.token_span(),
|
||||
is_token: Some(code.s1("is").token()),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
generic_list: None,
|
||||
port_list: None,
|
||||
end_ident_pos: Some(code.s("foo", 2).token())
|
||||
end_ident_pos: Some(code.s("foo", 2).token()),
|
||||
end_token: code.s1("end").token(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -191,6 +200,7 @@ end component;
|
|||
component,
|
||||
ComponentDeclaration {
|
||||
span: code.token_span(),
|
||||
is_token: Some(code.s1("is").token()),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
generic_list: Some(InterfaceList {
|
||||
interface_type: InterfaceType::Generic,
|
||||
|
|
@ -198,7 +208,8 @@ end component;
|
|||
span: code.between("generic", ");").token_span()
|
||||
}),
|
||||
port_list: None,
|
||||
end_ident_pos: None
|
||||
end_ident_pos: None,
|
||||
end_token: code.s1("end").token(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -219,6 +230,7 @@ end component;
|
|||
component,
|
||||
ComponentDeclaration {
|
||||
span: code.token_span(),
|
||||
is_token: Some(code.s1("is").token()),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
generic_list: None,
|
||||
port_list: Some(InterfaceList {
|
||||
|
|
@ -226,7 +238,8 @@ end component;
|
|||
items: vec![code.s1("foo : natural").port()],
|
||||
span: code.between("port", ");").token_span()
|
||||
}),
|
||||
end_ident_pos: None
|
||||
end_ident_pos: None,
|
||||
end_token: code.s1("end").token(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -250,7 +263,7 @@ end
|
|||
assert_eq!(
|
||||
diagnostics,
|
||||
vec![Diagnostic::syntax_error(
|
||||
&code.s("generic", 2).pos(),
|
||||
code.s("generic", 2).pos(),
|
||||
"Duplicate generic clause"
|
||||
)]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,20 +46,23 @@ pub fn parse_block_statement(
|
|||
_ => None,
|
||||
}
|
||||
};
|
||||
ctx.stream.pop_if_kind(Is);
|
||||
let is_token = ctx.stream.pop_if_kind(Is);
|
||||
let header = parse_block_header(ctx)?;
|
||||
let decl = parse_declarative_part(ctx)?;
|
||||
ctx.stream.expect_kind(Begin)?;
|
||||
let begin_token = ctx.stream.expect_kind(Begin)?;
|
||||
let statements = parse_labeled_concurrent_statements(ctx)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.expect_kind(Block)?;
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_tok = expect_semicolon_or_last(ctx);
|
||||
Ok(BlockStatement {
|
||||
guard_condition,
|
||||
is_token,
|
||||
header,
|
||||
decl,
|
||||
begin_token,
|
||||
statements,
|
||||
end_token,
|
||||
end_label_pos: check_label_identifier_mismatch(ctx, label, end_ident),
|
||||
span: TokenSpan::new(start_tok, end_tok),
|
||||
})
|
||||
|
|
@ -80,17 +83,17 @@ fn parse_block_header(ctx: &mut ParsingContext<'_>) -> ParseResult<BlockHeader>
|
|||
if let Some(map_token) = ctx.stream.pop_if_kind(Map) {
|
||||
if port_clause.is_some() || port_map.is_some() {
|
||||
ctx.diagnostics.push(Diagnostic::syntax_error(
|
||||
ctx.stream.get_token(map_token),
|
||||
ctx.stream.index(map_token),
|
||||
"Generic map must come before port clause and port map",
|
||||
));
|
||||
} else if generic_clause.is_none() {
|
||||
ctx.diagnostics.push(Diagnostic::syntax_error(
|
||||
ctx.stream.get_token(map_token),
|
||||
ctx.stream.index(map_token),
|
||||
"Generic map declared without preceding generic clause",
|
||||
));
|
||||
} else if generic_map.is_some() {
|
||||
ctx.diagnostics.push(Diagnostic::syntax_error(
|
||||
ctx.stream.get_token(map_token),
|
||||
ctx.stream.index(map_token),
|
||||
"Duplicate generic map",
|
||||
));
|
||||
}
|
||||
|
|
@ -98,9 +101,8 @@ fn parse_block_header(ctx: &mut ParsingContext<'_>) -> ParseResult<BlockHeader>
|
|||
expect_semicolon(ctx);
|
||||
if generic_map.is_none() {
|
||||
generic_map = Some(MapAspect {
|
||||
start: token_id,
|
||||
list,
|
||||
closing_paren,
|
||||
span: TokenSpan::new(token_id, closing_paren),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
|
@ -127,12 +129,12 @@ fn parse_block_header(ctx: &mut ParsingContext<'_>) -> ParseResult<BlockHeader>
|
|||
if let Some(map_token) = ctx.stream.pop_if_kind(Map) {
|
||||
if port_clause.is_none() {
|
||||
ctx.diagnostics.push(Diagnostic::syntax_error(
|
||||
ctx.stream.get_token(map_token),
|
||||
ctx.stream.index(map_token),
|
||||
"Port map declared without preceeding port clause",
|
||||
));
|
||||
} else if port_map.is_some() {
|
||||
ctx.diagnostics.push(Diagnostic::syntax_error(
|
||||
ctx.stream.get_token(map_token),
|
||||
ctx.stream.index(map_token),
|
||||
"Duplicate port map",
|
||||
));
|
||||
}
|
||||
|
|
@ -140,9 +142,8 @@ fn parse_block_header(ctx: &mut ParsingContext<'_>) -> ParseResult<BlockHeader>
|
|||
expect_semicolon(ctx);
|
||||
if port_map.is_none() {
|
||||
port_map = Some(MapAspect {
|
||||
start: token_id,
|
||||
span: TokenSpan::new(token_id, closing_paren),
|
||||
list,
|
||||
closing_paren,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
|
@ -183,19 +184,20 @@ pub fn parse_process_statement(
|
|||
postponed: Option<TokenId>,
|
||||
) -> ParseResult<ProcessStatement> {
|
||||
let process_token = ctx.stream.expect_kind(Process)?;
|
||||
let sensitivity_list = if ctx.stream.skip_if_kind(LeftPar) {
|
||||
let sensitivity_list = if let Some(left_par) = ctx.stream.pop_if_kind(LeftPar) {
|
||||
peek_token!(ctx.stream, token,
|
||||
All => {
|
||||
ctx.stream.skip();
|
||||
ctx.stream.expect_kind(RightPar)?;
|
||||
Some(SensitivityList::All)
|
||||
let right_par = ctx.stream.expect_kind(RightPar)?;
|
||||
Some(WithTokenSpan::new(SensitivityList::All, TokenSpan::new(left_par, right_par)))
|
||||
},
|
||||
RightPar => {
|
||||
let right_par = ctx.stream.get_current_token_id();
|
||||
ctx.stream.skip();
|
||||
ctx.diagnostics.push(
|
||||
Diagnostic::syntax_error(token, "Processes with sensitivity lists must contain at least one element.")
|
||||
);
|
||||
Some(SensitivityList::Names(Vec::new()))
|
||||
Some(WithTokenSpan::new(SensitivityList::Names(Vec::new()), TokenSpan::new(left_par, right_par)))
|
||||
},
|
||||
Identifier => {
|
||||
let mut names = Vec::with_capacity(1);
|
||||
|
|
@ -203,8 +205,9 @@ pub fn parse_process_statement(
|
|||
names.push(parse_name(ctx)?);
|
||||
peek_token!(ctx.stream, token,
|
||||
RightPar => {
|
||||
let right_par = ctx.stream.get_current_token_id();
|
||||
ctx.stream.skip();
|
||||
break Some(SensitivityList::Names(names));
|
||||
break Some(WithTokenSpan::new(SensitivityList::Names(names), TokenSpan::new(left_par, right_par)));
|
||||
},
|
||||
Comma => {
|
||||
ctx.stream.skip();
|
||||
|
|
@ -219,16 +222,16 @@ pub fn parse_process_statement(
|
|||
None
|
||||
};
|
||||
|
||||
ctx.stream.pop_if_kind(Is);
|
||||
let is_token = ctx.stream.pop_if_kind(Is);
|
||||
let decl = parse_declarative_part(ctx)?;
|
||||
ctx.stream.expect_kind(Begin)?;
|
||||
let begin_token = ctx.stream.expect_kind(Begin)?;
|
||||
let statements = parse_labeled_sequential_statements(ctx)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
|
||||
if let Some(token) = ctx.stream.pop_if_kind(Postponed) {
|
||||
if postponed.is_none() {
|
||||
ctx.diagnostics.push(Diagnostic::syntax_error(
|
||||
ctx.stream.get_token(token),
|
||||
ctx.stream.index(token),
|
||||
"'postponed' at the end of non-postponed process.",
|
||||
));
|
||||
}
|
||||
|
|
@ -239,8 +242,11 @@ pub fn parse_process_statement(
|
|||
Ok(ProcessStatement {
|
||||
postponed: postponed.is_some(),
|
||||
sensitivity_list,
|
||||
is_token,
|
||||
decl,
|
||||
begin_token,
|
||||
statements,
|
||||
end_token,
|
||||
end_label_pos: check_label_identifier_mismatch(ctx, label, end_ident),
|
||||
span: TokenSpan::new(postponed.unwrap_or(process_token), end_tok),
|
||||
})
|
||||
|
|
@ -261,7 +267,7 @@ fn to_procedure_call(
|
|||
call: WithTokenSpan::new(
|
||||
CallOrIndexed {
|
||||
name: WithTokenSpan::new(name, target.span),
|
||||
parameters: vec![],
|
||||
parameters: SeparatedList::default(),
|
||||
},
|
||||
target.span,
|
||||
),
|
||||
|
|
@ -352,9 +358,8 @@ pub fn parse_map_aspect(
|
|||
ctx.stream.expect_kind(Map)?;
|
||||
let (list, closing_paren) = parse_association_list(ctx)?;
|
||||
Ok(Some(MapAspect {
|
||||
start: aspect,
|
||||
span: TokenSpan::new(aspect, closing_paren),
|
||||
list,
|
||||
closing_paren,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
|
@ -389,11 +394,11 @@ pub fn parse_instantiation_statement(
|
|||
|
||||
fn parse_optional_declarative_part(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
) -> ParseResult<Option<Vec<WithTokenSpan<Declaration>>>> {
|
||||
) -> ParseResult<Option<(Vec<WithTokenSpan<Declaration>>, TokenId)>> {
|
||||
if is_declarative_part(ctx)? {
|
||||
let decls = parse_declarative_part(ctx)?;
|
||||
ctx.stream.expect_kind(Begin)?;
|
||||
Ok(Some(decls))
|
||||
let begin_token = ctx.stream.expect_kind(Begin)?;
|
||||
Ok(Some((decls, begin_token)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
|
@ -405,30 +410,37 @@ fn parse_generate_body(
|
|||
) -> ParseResult<GenerateBody> {
|
||||
let decl = parse_optional_declarative_part(ctx)?;
|
||||
let statements = parse_labeled_concurrent_statements(ctx)?;
|
||||
let mut end_label_pos = None;
|
||||
let mut end_label = None;
|
||||
|
||||
let end_token;
|
||||
// Potential inner end [ alternative_label ];
|
||||
if ctx.stream.next_kinds_are(&[End, SemiColon]) {
|
||||
end_token = Some(ctx.stream.get_current_token_id());
|
||||
// Inner end no label
|
||||
ctx.stream.skip();
|
||||
ctx.stream.skip();
|
||||
} else if ctx.stream.next_kinds_are(&[End, Identifier]) {
|
||||
end_token = Some(ctx.stream.get_current_token_id());
|
||||
ctx.stream.skip();
|
||||
// Inner with identifier
|
||||
let end_ident = ctx.stream.expect_ident()?;
|
||||
end_label_pos = check_label_identifier_mismatch(
|
||||
end_label = Some(ctx.stream.get_last_token_id());
|
||||
check_label_identifier_mismatch(
|
||||
ctx,
|
||||
alternative_label.as_ref().map(|label| &label.tree),
|
||||
Some(end_ident),
|
||||
);
|
||||
expect_semicolon(ctx);
|
||||
} else {
|
||||
end_token = None
|
||||
}
|
||||
|
||||
let body = GenerateBody {
|
||||
alternative_label,
|
||||
decl,
|
||||
statements,
|
||||
end_label_pos,
|
||||
end_token,
|
||||
end_label,
|
||||
};
|
||||
|
||||
Ok(body)
|
||||
|
|
@ -443,9 +455,9 @@ fn parse_for_generate_statement(
|
|||
let index_name = WithDecl::new(ctx.stream.expect_ident()?);
|
||||
ctx.stream.expect_kind(In)?;
|
||||
let discrete_range = parse_discrete_range(ctx)?;
|
||||
ctx.stream.expect_kind(Generate)?;
|
||||
let generate_token = ctx.stream.expect_kind(Generate)?;
|
||||
let body = parse_generate_body(ctx, None)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.expect_kind(Generate)?;
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_tok = expect_semicolon_or_last(ctx);
|
||||
|
|
@ -453,7 +465,9 @@ fn parse_for_generate_statement(
|
|||
Ok(ForGenerateStatement {
|
||||
index_name,
|
||||
discrete_range,
|
||||
generate_token,
|
||||
body,
|
||||
end_token,
|
||||
end_label_pos: check_label_identifier_mismatch(ctx, label, end_ident),
|
||||
span: TokenSpan::new(start_tok, end_tok),
|
||||
})
|
||||
|
|
@ -486,7 +500,7 @@ fn parse_if_generate_statement(
|
|||
conditionals.push(conditional);
|
||||
|
||||
expect_token!(
|
||||
ctx.stream, end_token,
|
||||
ctx.stream, end_token, token_id,
|
||||
End => {
|
||||
else_branch = None;
|
||||
break;
|
||||
|
|
@ -510,7 +524,7 @@ fn parse_if_generate_statement(
|
|||
);
|
||||
let body = parse_generate_body(ctx, alternative_label)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
else_branch = Some(body);
|
||||
else_branch = Some((body, token_id));
|
||||
break;
|
||||
}
|
||||
);
|
||||
|
|
@ -541,7 +555,8 @@ fn parse_case_generate_statement(
|
|||
ctx.stream.expect_kind(When)?;
|
||||
|
||||
let mut alternatives = Vec::with_capacity(2);
|
||||
loop {
|
||||
let end_token = loop {
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
let alternative_label = {
|
||||
if ctx.stream.next_kinds_are(&[Identifier, Colon]) {
|
||||
let ident = ctx.stream.expect_ident()?;
|
||||
|
|
@ -554,18 +569,20 @@ fn parse_case_generate_statement(
|
|||
let choices = parse_choices(ctx)?;
|
||||
ctx.stream.expect_kind(RightArrow)?;
|
||||
let body = parse_generate_body(ctx, alternative_label)?;
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
|
||||
alternatives.push(Alternative {
|
||||
choices,
|
||||
item: body,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
});
|
||||
|
||||
expect_token!(
|
||||
ctx.stream, end_token,
|
||||
End => break,
|
||||
ctx.stream, end_token, end_token_id,
|
||||
End => break end_token_id,
|
||||
When => continue
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ctx.stream.expect_kind(Generate)?;
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
|
|
@ -577,6 +594,7 @@ fn parse_case_generate_statement(
|
|||
alternatives,
|
||||
},
|
||||
end_label_pos: check_label_identifier_mismatch(ctx, label, end_ident),
|
||||
end_token,
|
||||
span: TokenSpan::new(start_tok, end_tok),
|
||||
})
|
||||
}
|
||||
|
|
@ -822,6 +840,7 @@ end block;",
|
|||
|
||||
let block = BlockStatement {
|
||||
guard_condition: None,
|
||||
is_token: None,
|
||||
header: BlockHeader {
|
||||
generic_clause: None,
|
||||
generic_map: None,
|
||||
|
|
@ -829,6 +848,7 @@ end block;",
|
|||
port_map: None,
|
||||
},
|
||||
decl: code.s1("constant const : natural := 0;").declarative_part(),
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![LabeledConcurrentStatement {
|
||||
label: Some(code.s1("name2").ident()).into(),
|
||||
statement: WithTokenSpan::new(
|
||||
|
|
@ -836,6 +856,7 @@ end block;",
|
|||
code.s1("foo(clk);").token_span(),
|
||||
),
|
||||
}],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("block").token()),
|
||||
};
|
||||
|
|
@ -860,6 +881,7 @@ end block name;",
|
|||
);
|
||||
let block = BlockStatement {
|
||||
guard_condition: None,
|
||||
is_token: Some(code.s1("is").token()),
|
||||
header: BlockHeader {
|
||||
generic_clause: None,
|
||||
generic_map: None,
|
||||
|
|
@ -867,7 +889,9 @@ end block name;",
|
|||
port_map: None,
|
||||
},
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: Some(code.s("name", 2).pos()),
|
||||
span: code.token_span().skip_to(code.s1("block").token()),
|
||||
};
|
||||
|
|
@ -898,8 +922,11 @@ end block;",
|
|||
port_clause: None,
|
||||
port_map: None,
|
||||
},
|
||||
is_token: None,
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("block").token()),
|
||||
};
|
||||
|
|
@ -930,8 +957,11 @@ end block;",
|
|||
port_clause: None,
|
||||
port_map: None,
|
||||
},
|
||||
is_token: Some(code.s1("is").token()),
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("block").token()),
|
||||
};
|
||||
|
|
@ -974,8 +1004,11 @@ end block;",
|
|||
}),
|
||||
port_map: Some(code.s1("port map(prt => 2)").port_map_aspect()),
|
||||
},
|
||||
is_token: Some(code.s1("is").token()),
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("block").token()),
|
||||
};
|
||||
|
|
@ -1001,8 +1034,11 @@ end process;",
|
|||
let process = ProcessStatement {
|
||||
postponed: false,
|
||||
sensitivity_list: None,
|
||||
is_token: None,
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1025,8 +1061,11 @@ end process name;",
|
|||
let process = ProcessStatement {
|
||||
postponed: false,
|
||||
sensitivity_list: None,
|
||||
is_token: Some(code.s1("is").token()),
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: Some(code.s("name", 2).pos()),
|
||||
span: code.token_span().skip_to(code.s1("process").token()),
|
||||
};
|
||||
|
|
@ -1052,8 +1091,11 @@ end process;",
|
|||
let process = ProcessStatement {
|
||||
postponed: true,
|
||||
sensitivity_list: None,
|
||||
is_token: None,
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1076,8 +1118,11 @@ end postponed process;",
|
|||
let process = ProcessStatement {
|
||||
postponed: true,
|
||||
sensitivity_list: None,
|
||||
is_token: None,
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1101,8 +1146,11 @@ end postponed process;",
|
|||
let process = ProcessStatement {
|
||||
postponed: false,
|
||||
sensitivity_list: None,
|
||||
is_token: Some(code.s1("is").token()),
|
||||
decl: Vec::new(),
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: Vec::new(),
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1129,12 +1177,15 @@ end process;",
|
|||
);
|
||||
let process = ProcessStatement {
|
||||
postponed: false,
|
||||
sensitivity_list: Some(SensitivityList::Names(vec![
|
||||
code.s1("clk").name(),
|
||||
code.s1("vec(1)").name(),
|
||||
])),
|
||||
sensitivity_list: Some(WithTokenSpan::new(
|
||||
SensitivityList::Names(vec![code.s1("clk").name(), code.s1("vec(1)").name()]),
|
||||
code.between("(", "))").token_span(),
|
||||
)),
|
||||
is_token: Some(code.s1("is").token()),
|
||||
decl: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1157,9 +1208,15 @@ end process;",
|
|||
let (stmt, diagnostics) = code.with_stream_diagnostics(parse_labeled_concurrent_statement);
|
||||
let process = ProcessStatement {
|
||||
postponed: false,
|
||||
sensitivity_list: Some(SensitivityList::Names(Vec::new())),
|
||||
sensitivity_list: Some(WithTokenSpan::new(
|
||||
SensitivityList::Names(Vec::new()),
|
||||
code.s1("()").token_span(),
|
||||
)),
|
||||
is_token: Some(code.s1("is").token()),
|
||||
decl: Vec::new(),
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: Vec::new(),
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1189,12 +1246,18 @@ end process;",
|
|||
);
|
||||
let process = ProcessStatement {
|
||||
postponed: false,
|
||||
sensitivity_list: Some(SensitivityList::All),
|
||||
sensitivity_list: Some(WithTokenSpan::new(
|
||||
SensitivityList::All,
|
||||
code.s1("(all)").token_span(),
|
||||
)),
|
||||
is_token: Some(code.s1("is").token()),
|
||||
decl: code.s1("variable foo : boolean;").declarative_part(),
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![
|
||||
code.s1("foo <= true;").sequential_statement(),
|
||||
code.s1("wait;").sequential_statement(),
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1300,6 +1363,7 @@ with x(0) + 1 select
|
|||
alternatives: vec![Alternative {
|
||||
choices: code.s1("0|1").choices(),
|
||||
item: code.s1("bar(1,2) after 2 ns").waveform(),
|
||||
span: code.s1("bar(1,2) after 2 ns when 0|1").token_span(),
|
||||
}],
|
||||
};
|
||||
|
||||
|
|
@ -1312,7 +1376,10 @@ with x(0) + 1 select
|
|||
guarded: false,
|
||||
assignment: SignalAssignment {
|
||||
target: code.s1("foo(0)").name().map_into(Target::Name),
|
||||
delay_mechanism: Some(DelayMechanism::Transport),
|
||||
delay_mechanism: Some(WithTokenSpan::new(
|
||||
DelayMechanism::Transport,
|
||||
code.s1("transport").token_span()
|
||||
)),
|
||||
rhs: AssignmentRightHand::Selected(selection)
|
||||
}
|
||||
})
|
||||
|
|
@ -1521,12 +1588,15 @@ end generate;",
|
|||
let gen = ForGenerateStatement {
|
||||
index_name: code.s1("idx").decl_ident(),
|
||||
discrete_range: code.s1("0 to 1").discrete_range(),
|
||||
generate_token: code.s1("generate").token(),
|
||||
body: GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("for").token()),
|
||||
};
|
||||
|
|
@ -1552,13 +1622,16 @@ end generate;",
|
|||
let gen = ForGenerateStatement {
|
||||
index_name: code.s1("idx").decl_ident(),
|
||||
discrete_range: code.s1("0 to 1").discrete_range(),
|
||||
generate_token: code.s1("generate").token(),
|
||||
body: GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![code.s1("foo <= bar;").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
end_label_pos: None,
|
||||
end_token: code.s1("end").token(),
|
||||
span: code.token_span().skip_to(code.s1("for").token()),
|
||||
};
|
||||
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
|
||||
|
|
@ -1574,16 +1647,24 @@ end generate;",
|
|||
|
||||
#[test]
|
||||
fn test_for_generate_empty_declarations() {
|
||||
fn test(decl: Option<Vec<WithTokenSpan<Declaration>>>, code: Code) {
|
||||
fn test(
|
||||
decl: Option<Vec<WithTokenSpan<Declaration>>>,
|
||||
code: Code,
|
||||
body_end_token: Option<usize>,
|
||||
end_token: usize,
|
||||
) {
|
||||
let gen = ForGenerateStatement {
|
||||
index_name: code.s1("idx").decl_ident(),
|
||||
discrete_range: code.s1("0 to 1").discrete_range(),
|
||||
generate_token: code.s1("generate").token(),
|
||||
body: GenerateBody {
|
||||
alternative_label: None,
|
||||
decl,
|
||||
decl: decl.map(|val| (val, code.s1("begin").token())),
|
||||
statements: vec![code.s1("foo <= bar;").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: body_end_token.map(|occ| code.s("end", occ).token()),
|
||||
end_label: None,
|
||||
},
|
||||
end_token: code.s("end", end_token).token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("for").token()),
|
||||
};
|
||||
|
|
@ -1606,6 +1687,8 @@ begin
|
|||
foo <= bar;
|
||||
end generate;",
|
||||
),
|
||||
None,
|
||||
1,
|
||||
);
|
||||
|
||||
test(
|
||||
|
|
@ -1616,6 +1699,8 @@ gen: for idx in 0 to 1 generate
|
|||
foo <= bar;
|
||||
end generate;",
|
||||
),
|
||||
None,
|
||||
1,
|
||||
);
|
||||
|
||||
test(
|
||||
|
|
@ -1628,21 +1713,29 @@ begin
|
|||
end;
|
||||
end generate;",
|
||||
),
|
||||
Some(1),
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_for_generate_declarations() {
|
||||
fn test(code: Code) {
|
||||
fn test(code: Code, end_occurrence: usize, body_end_token: Option<usize>) {
|
||||
let gen = ForGenerateStatement {
|
||||
index_name: code.s1("idx").decl_ident(),
|
||||
discrete_range: code.s1("0 to 1").discrete_range(),
|
||||
generate_token: code.s1("generate").token(),
|
||||
body: GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: Some(code.s1("signal foo : natural;").declarative_part()),
|
||||
decl: Some((
|
||||
code.s1("signal foo : natural;").declarative_part(),
|
||||
code.s1("begin").token(),
|
||||
)),
|
||||
statements: vec![code.s1("foo <= bar;").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: body_end_token.map(|val| code.s("end", val).token()),
|
||||
end_label: None,
|
||||
},
|
||||
end_token: code.s("end", end_occurrence).token(),
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("for").token()),
|
||||
};
|
||||
|
|
@ -1657,24 +1750,32 @@ end generate;",
|
|||
);
|
||||
}
|
||||
|
||||
test(Code::new(
|
||||
"\
|
||||
test(
|
||||
Code::new(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
signal foo : natural;
|
||||
begin
|
||||
foo <= bar;
|
||||
end generate;",
|
||||
));
|
||||
),
|
||||
1,
|
||||
None,
|
||||
);
|
||||
|
||||
test(Code::new(
|
||||
"\
|
||||
test(
|
||||
Code::new(
|
||||
"\
|
||||
gen: for idx in 0 to 1 generate
|
||||
signal foo : natural;
|
||||
begin
|
||||
foo <= bar;
|
||||
end;
|
||||
end generate;",
|
||||
));
|
||||
),
|
||||
2,
|
||||
Some(1),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1692,7 +1793,8 @@ end generate;",
|
|||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
end_label: None,
|
||||
end_token: None,
|
||||
},
|
||||
}],
|
||||
else_item: None,
|
||||
|
|
@ -1725,9 +1827,10 @@ end generate;",
|
|||
condition: code.s1("cond = true").expr(),
|
||||
item: GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: Some(vec![]),
|
||||
decl: Some((vec![], code.s1("begin").token())),
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
}],
|
||||
else_item: None,
|
||||
|
|
@ -1764,7 +1867,8 @@ end generate;",
|
|||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
},
|
||||
Conditional {
|
||||
|
|
@ -1773,16 +1877,21 @@ end generate;",
|
|||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
else_item: Some(GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
}),
|
||||
else_item: Some((
|
||||
GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
code.s1("else").token(),
|
||||
)),
|
||||
},
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("if").token()),
|
||||
|
|
@ -1822,27 +1931,42 @@ end generate;",
|
|||
condition: code.s1("cond = true").expr(),
|
||||
item: GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: Some(code.s1("variable v1 : boolean;").declarative_part()),
|
||||
decl: Some((
|
||||
code.s1("variable v1 : boolean;").declarative_part(),
|
||||
code.s("begin", 1).token(),
|
||||
)),
|
||||
statements: vec![code.s1("foo1(clk);").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
},
|
||||
Conditional {
|
||||
condition: code.s1("cond2 = true").expr(),
|
||||
item: GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: Some(code.s1("variable v2 : boolean;").declarative_part()),
|
||||
decl: Some((
|
||||
code.s1("variable v2 : boolean;").declarative_part(),
|
||||
code.s("begin", 2).token(),
|
||||
)),
|
||||
statements: vec![code.s1("foo2(clk);").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
else_item: Some(GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: Some(code.s1("variable v3 : boolean;").declarative_part()),
|
||||
statements: vec![code.s1("foo3(clk);").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
}),
|
||||
else_item: Some((
|
||||
GenerateBody {
|
||||
alternative_label: None,
|
||||
decl: Some((
|
||||
code.s1("variable v3 : boolean;").declarative_part(),
|
||||
code.s("begin", 3).token(),
|
||||
)),
|
||||
statements: vec![code.s1("foo3(clk);").concurrent_statement()],
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
code.s1("else").token(),
|
||||
)),
|
||||
},
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("if").token()),
|
||||
|
|
@ -1878,7 +2002,8 @@ end generate;",
|
|||
alternative_label: Some(code.s1("alt1").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
},
|
||||
Conditional {
|
||||
|
|
@ -1887,16 +2012,21 @@ end generate;",
|
|||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
end_token: Some(code.s("end", 1).token()),
|
||||
end_label: Some(code.s1("alt2").token()),
|
||||
},
|
||||
},
|
||||
],
|
||||
else_item: Some(GenerateBody {
|
||||
alternative_label: Some(code.s1("alt3").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: None,
|
||||
}),
|
||||
else_item: Some((
|
||||
GenerateBody {
|
||||
alternative_label: Some(code.s1("alt3").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_token: Some(code.s("end", 2).token()),
|
||||
end_label: Some(code.s1("alt4").token()),
|
||||
},
|
||||
code.s1("else").token(),
|
||||
)),
|
||||
},
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("if").token()),
|
||||
|
|
@ -1942,7 +2072,8 @@ end generate;",
|
|||
alternative_label: Some(code.s1("alt1").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: Some(code.s("alt1", 2).pos()),
|
||||
end_token: Some(code.s("end", 1).token()),
|
||||
end_label: Some(code.s("alt1", 2).token()),
|
||||
},
|
||||
},
|
||||
Conditional {
|
||||
|
|
@ -1951,16 +2082,21 @@ end generate;",
|
|||
alternative_label: Some(code.s1("alt2").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: Some(code.s("alt2", 2).pos()),
|
||||
end_token: Some(code.s("end", 2).token()),
|
||||
end_label: Some(code.s("alt2", 2).token()),
|
||||
},
|
||||
},
|
||||
],
|
||||
else_item: Some(GenerateBody {
|
||||
alternative_label: Some(code.s1("alt3").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_label_pos: Some(code.s("alt3", 2).pos()),
|
||||
}),
|
||||
else_item: Some((
|
||||
GenerateBody {
|
||||
alternative_label: Some(code.s1("alt3").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![],
|
||||
end_token: Some(code.s("end", 3).token()),
|
||||
end_label: Some(code.s("alt3", 2).token()),
|
||||
},
|
||||
code.s1("else").token(),
|
||||
)),
|
||||
},
|
||||
end_label_pos: None,
|
||||
span: code.token_span().skip_to(code.s1("if").token()),
|
||||
|
|
@ -1997,8 +2133,10 @@ end generate;",
|
|||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![code.s1("sig <= value;").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
span: code.between("1 | 2", "value;").token_span(),
|
||||
},
|
||||
Alternative {
|
||||
choices: code.s1("others").choices(),
|
||||
|
|
@ -2006,12 +2144,15 @@ end generate;",
|
|||
alternative_label: None,
|
||||
decl: None,
|
||||
statements: vec![code.s1("foo(clk);").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
span: code.between("others", "foo(clk);").token_span(),
|
||||
},
|
||||
],
|
||||
},
|
||||
end_label_pos: None,
|
||||
end_token: code.s1("end").token(),
|
||||
span: code.token_span().skip_to(code.s1("case").token()),
|
||||
};
|
||||
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
|
||||
|
|
@ -2046,8 +2187,10 @@ end generate gen1;",
|
|||
alternative_label: Some(code.s1("alt1").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![code.s1("sig <= value;").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
span: code.between("alt1", ";").token_span(),
|
||||
},
|
||||
Alternative {
|
||||
choices: code.s1("others").choices(),
|
||||
|
|
@ -2055,12 +2198,15 @@ end generate gen1;",
|
|||
alternative_label: Some(code.s1("alt2").decl_ident()),
|
||||
decl: None,
|
||||
statements: vec![code.s1("foo(clk);").concurrent_statement()],
|
||||
end_label_pos: None,
|
||||
end_token: None,
|
||||
end_label: None,
|
||||
},
|
||||
span: code.between("alt2", ";").token_span(),
|
||||
},
|
||||
],
|
||||
},
|
||||
end_label_pos: Some(code.s("gen1", 2).pos()),
|
||||
end_token: code.s1("end").token(),
|
||||
span: code.token_span().skip_to(code.s1("case").token()),
|
||||
};
|
||||
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@ use crate::ast::*;
|
|||
use crate::data::*;
|
||||
use crate::syntax::recover::{expect_semicolon, expect_semicolon_or_last};
|
||||
use vhdl_lang::syntax::parser::ParsingContext;
|
||||
use vhdl_lang::TokenId;
|
||||
|
||||
/// LRM 7.3.2.2
|
||||
fn parse_entity_aspect(ctx: &mut ParsingContext<'_>) -> ParseResult<EntityAspect> {
|
||||
pub(crate) fn parse_entity_aspect(ctx: &mut ParsingContext<'_>) -> ParseResult<EntityAspect> {
|
||||
let entity_aspect = expect_token!(
|
||||
ctx.stream,
|
||||
token,
|
||||
|
|
@ -43,25 +44,27 @@ fn parse_entity_aspect(ctx: &mut ParsingContext<'_>) -> ParseResult<EntityAspect
|
|||
fn parse_binding_indication_known_entity_aspect(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
entity_aspect: Option<EntityAspect>,
|
||||
start_token: TokenId,
|
||||
) -> ParseResult<BindingIndication> {
|
||||
let (generic_map, port_map) = parse_generic_and_port_map(ctx)?;
|
||||
|
||||
expect_semicolon(ctx);
|
||||
let semicolon_token = expect_semicolon_or_last(ctx);
|
||||
Ok(BindingIndication {
|
||||
entity_aspect,
|
||||
generic_map,
|
||||
port_map,
|
||||
span: TokenSpan::new(start_token, semicolon_token),
|
||||
})
|
||||
}
|
||||
|
||||
/// LRM 7.3.2
|
||||
fn parse_binding_indication(ctx: &mut ParsingContext<'_>) -> ParseResult<BindingIndication> {
|
||||
let entity_aspect = if ctx.stream.skip_if_kind(Use) {
|
||||
Some(parse_entity_aspect(ctx)?)
|
||||
let (entity_aspect, token) = if let Some(use_token) = ctx.stream.pop_if_kind(Use) {
|
||||
(Some(parse_entity_aspect(ctx)?), use_token)
|
||||
} else {
|
||||
None
|
||||
(None, ctx.stream.get_current_token_id())
|
||||
};
|
||||
parse_binding_indication_known_entity_aspect(ctx, entity_aspect)
|
||||
parse_binding_indication_known_entity_aspect(ctx, entity_aspect, token)
|
||||
}
|
||||
|
||||
fn parse_component_configuration_known_spec(
|
||||
|
|
@ -71,19 +74,20 @@ fn parse_component_configuration_known_spec(
|
|||
let (bind_ind, vunit_bind_inds) = peek_token!(
|
||||
ctx.stream,
|
||||
token,
|
||||
token_id,
|
||||
End => (None, Vec::new()),
|
||||
For => (None, Vec::new()),
|
||||
Use => {
|
||||
ctx.stream.skip();
|
||||
if ctx.stream.peek_kind() == Some(Vunit) {
|
||||
let vunit_bind_inds = parse_vunit_binding_indication_list_known_keyword(ctx)?;
|
||||
let vunit_bind_inds = parse_vunit_binding_indication_list_known_keyword(ctx, token_id)?;
|
||||
(None, vunit_bind_inds)
|
||||
} else {
|
||||
let aspect = parse_entity_aspect(ctx)?;
|
||||
let bind_ind = parse_binding_indication_known_entity_aspect(ctx, Some(aspect))?;
|
||||
let bind_ind = parse_binding_indication_known_entity_aspect(ctx, Some(aspect), token_id)?;
|
||||
|
||||
if ctx.stream.skip_if_kind(Use) {
|
||||
(Some(bind_ind), parse_vunit_binding_indication_list_known_keyword(ctx)?)
|
||||
if let Some(start_token) = ctx.stream.pop_if_kind(Use) {
|
||||
(Some(bind_ind), parse_vunit_binding_indication_list_known_keyword(ctx, start_token)?)
|
||||
} else {
|
||||
(Some(bind_ind), Vec::new())
|
||||
}
|
||||
|
|
@ -94,21 +98,24 @@ fn parse_component_configuration_known_spec(
|
|||
let block_config = expect_token!(
|
||||
ctx.stream,
|
||||
token,
|
||||
token_id,
|
||||
End => None,
|
||||
For => {
|
||||
let block_config = parse_block_configuration_known_keyword(ctx)?;
|
||||
let block_config = parse_block_configuration_known_keyword(ctx, token_id)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
Some(block_config)
|
||||
}
|
||||
);
|
||||
|
||||
ctx.stream.expect_kind(For)?;
|
||||
expect_semicolon(ctx);
|
||||
let semicolon_token = expect_semicolon_or_last(ctx);
|
||||
let start_token = spec.span.start_token;
|
||||
Ok(ComponentConfiguration {
|
||||
spec,
|
||||
bind_ind,
|
||||
vunit_bind_inds,
|
||||
block_config,
|
||||
span: TokenSpan::new(start_token, semicolon_token),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -119,26 +126,33 @@ enum ComponentSpecificationOrName {
|
|||
|
||||
fn parse_component_specification_or_name(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
start_token: TokenId,
|
||||
) -> ParseResult<ComponentSpecificationOrName> {
|
||||
peek_token!(
|
||||
ctx.stream, token,
|
||||
All => {
|
||||
ctx.stream.skip();
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let component_name = parse_selected_name(ctx)?;
|
||||
let end_token = component_name.span.end_token;
|
||||
Ok(ComponentSpecificationOrName::ComponentSpec(ComponentSpecification {
|
||||
instantiation_list: InstantiationList::All,
|
||||
component_name,
|
||||
colon_token,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
}))
|
||||
|
||||
},
|
||||
Others => {
|
||||
ctx.stream.skip();
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let component_name = parse_selected_name(ctx)?;
|
||||
let end_token = component_name.span.end_token;
|
||||
Ok(ComponentSpecificationOrName::ComponentSpec(ComponentSpecification {
|
||||
instantiation_list: InstantiationList::Others,
|
||||
component_name,
|
||||
colon_token,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
}))
|
||||
},
|
||||
Identifier => {
|
||||
|
|
@ -146,30 +160,38 @@ fn parse_component_specification_or_name(
|
|||
let sep_token = ctx.stream.peek_expect()?;
|
||||
match sep_token.kind {
|
||||
Colon => {
|
||||
let colon_token = ctx.stream.get_current_token_id();
|
||||
ctx.stream.skip();
|
||||
let ident = to_simple_name(ctx.stream, name)?;
|
||||
let component_name = parse_selected_name(ctx)?;
|
||||
let end_token = component_name.span.end_token;
|
||||
Ok(ComponentSpecificationOrName::ComponentSpec(ComponentSpecification {
|
||||
instantiation_list: InstantiationList::Labels(vec![ident]),
|
||||
component_name,
|
||||
colon_token,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
}))
|
||||
}
|
||||
Comma => {
|
||||
ctx.stream.skip();
|
||||
let mut idents = vec![to_simple_name(ctx.stream, name)?];
|
||||
loop {
|
||||
let colon_token = loop {
|
||||
idents.push(ctx.stream.expect_ident()?);
|
||||
expect_token!(
|
||||
ctx.stream,
|
||||
next_token,
|
||||
next_token_id,
|
||||
Comma => {},
|
||||
Colon => break
|
||||
Colon => break next_token_id
|
||||
);
|
||||
}
|
||||
};
|
||||
let component_name = parse_selected_name(ctx)?;
|
||||
let end_token = component_name.span.end_token;
|
||||
Ok(ComponentSpecificationOrName::ComponentSpec(ComponentSpecification {
|
||||
instantiation_list: InstantiationList::Labels(idents),
|
||||
component_name,
|
||||
colon_token,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
}))
|
||||
}
|
||||
_ => Ok(ComponentSpecificationOrName::Name(name))
|
||||
|
|
@ -180,15 +202,16 @@ fn parse_component_specification_or_name(
|
|||
|
||||
fn parse_configuration_item_known_keyword(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
token: TokenId,
|
||||
) -> ParseResult<ConfigurationItem> {
|
||||
match parse_component_specification_or_name(ctx)? {
|
||||
match parse_component_specification_or_name(ctx, token)? {
|
||||
ComponentSpecificationOrName::ComponentSpec(component_spec) => {
|
||||
Ok(ConfigurationItem::Component(
|
||||
parse_component_configuration_known_spec(ctx, component_spec)?,
|
||||
))
|
||||
}
|
||||
ComponentSpecificationOrName::Name(name) => Ok(ConfigurationItem::Block(
|
||||
parse_block_configuration_known_name(ctx, name)?,
|
||||
parse_block_configuration_known_name(ctx, name, token)?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
@ -196,6 +219,7 @@ fn parse_configuration_item_known_keyword(
|
|||
fn parse_block_configuration_known_name(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
name: WithTokenSpan<Name>,
|
||||
start_token: TokenId,
|
||||
) -> ParseResult<BlockConfiguration> {
|
||||
let block_spec = name;
|
||||
// @TODO use clauses
|
||||
|
|
@ -206,32 +230,36 @@ fn parse_block_configuration_known_name(
|
|||
expect_token!(
|
||||
ctx.stream,
|
||||
token,
|
||||
token_id,
|
||||
End => {
|
||||
break;
|
||||
},
|
||||
For => {
|
||||
items.push(parse_configuration_item_known_keyword(ctx)?);
|
||||
items.push(parse_configuration_item_known_keyword(ctx, token_id)?);
|
||||
}
|
||||
);
|
||||
}
|
||||
ctx.stream.expect_kind(For)?;
|
||||
expect_semicolon(ctx);
|
||||
let semicolon = expect_semicolon_or_last(ctx);
|
||||
Ok(BlockConfiguration {
|
||||
block_spec,
|
||||
use_clauses,
|
||||
items,
|
||||
span: TokenSpan::new(start_token, semicolon),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_block_configuration_known_keyword(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
token: TokenId,
|
||||
) -> ParseResult<BlockConfiguration> {
|
||||
let name = parse_name(ctx)?;
|
||||
parse_block_configuration_known_name(ctx, name)
|
||||
parse_block_configuration_known_name(ctx, name, token)
|
||||
}
|
||||
|
||||
fn parse_vunit_binding_indication_list_known_keyword(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
start_token: TokenId,
|
||||
) -> ParseResult<Vec<VUnitBindingIndication>> {
|
||||
let mut indications = Vec::new();
|
||||
loop {
|
||||
|
|
@ -242,13 +270,16 @@ fn parse_vunit_binding_indication_list_known_keyword(
|
|||
let vunit_bind_ind = loop {
|
||||
vunit_list.push(parse_name(ctx)?);
|
||||
peek_token!(
|
||||
ctx.stream, token,
|
||||
ctx.stream, token, token_id,
|
||||
Comma => {
|
||||
ctx.stream.skip();
|
||||
},
|
||||
SemiColon => {
|
||||
ctx.stream.skip();
|
||||
break VUnitBindingIndication { vunit_list };
|
||||
break VUnitBindingIndication {
|
||||
vunit_list,
|
||||
span: TokenSpan::new(start_token, token_id)
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
@ -278,28 +309,27 @@ pub fn parse_configuration_declaration(
|
|||
match token.kind {
|
||||
Use => {
|
||||
if ctx.stream.nth_kind_is(1, Vunit) {
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
ctx.stream.skip();
|
||||
break parse_vunit_binding_indication_list_known_keyword(ctx)?;
|
||||
break parse_vunit_binding_indication_list_known_keyword(ctx, start_token)?;
|
||||
}
|
||||
|
||||
decl.push(ConfigurationDeclarativeItem::Use(
|
||||
parse_use_clause(ctx)?.item,
|
||||
));
|
||||
decl.push(parse_use_clause(ctx)?.map_into(Declaration::Use));
|
||||
}
|
||||
_ => break Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
ctx.stream.expect_kind(For)?;
|
||||
let block_config = parse_block_configuration_known_keyword(ctx)?;
|
||||
let for_token = ctx.stream.expect_kind(For)?;
|
||||
let block_config = parse_block_configuration_known_keyword(ctx, for_token)?;
|
||||
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.pop_if_kind(Configuration);
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let last_token = expect_semicolon_or_last(ctx);
|
||||
|
||||
Ok(ConfigurationDeclaration {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, last_token),
|
||||
context_clause: ContextClause::default(),
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, &ident.tree, end_ident),
|
||||
ident,
|
||||
|
|
@ -307,6 +337,7 @@ pub fn parse_configuration_declaration(
|
|||
decl,
|
||||
vunit_bind_inds,
|
||||
block_config,
|
||||
end_token,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -315,30 +346,36 @@ pub fn parse_configuration_specification(
|
|||
ctx: &mut ParsingContext<'_>,
|
||||
) -> ParseResult<ConfigurationSpecification> {
|
||||
let start_token = ctx.stream.expect_kind(For)?;
|
||||
match parse_component_specification_or_name(ctx)? {
|
||||
match parse_component_specification_or_name(ctx, start_token)? {
|
||||
ComponentSpecificationOrName::ComponentSpec(spec) => {
|
||||
let bind_ind = parse_binding_indication(ctx)?;
|
||||
if ctx.stream.skip_if_kind(Use) {
|
||||
let vunit_bind_inds = parse_vunit_binding_indication_list_known_keyword(ctx)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
if let Some(use_token) = ctx.stream.pop_if_kind(Use) {
|
||||
let vunit_bind_inds =
|
||||
parse_vunit_binding_indication_list_known_keyword(ctx, use_token)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.expect_kind(For)?;
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let final_token = expect_semicolon_or_last(ctx);
|
||||
Ok(ConfigurationSpecification {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, final_token),
|
||||
spec,
|
||||
bind_ind,
|
||||
vunit_bind_inds,
|
||||
end_token: Some(end_token),
|
||||
})
|
||||
} else {
|
||||
if ctx.stream.skip_if_kind(End) {
|
||||
let end_token = if let Some(token) = ctx.stream.pop_if_kind(End) {
|
||||
ctx.stream.expect_kind(For)?;
|
||||
expect_semicolon(ctx);
|
||||
}
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
Some(token)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let final_token = ctx.stream.get_last_token_id();
|
||||
Ok(ConfigurationSpecification {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, final_token),
|
||||
spec,
|
||||
bind_ind,
|
||||
end_token,
|
||||
vunit_bind_inds: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
|
@ -378,7 +415,9 @@ end;
|
|||
block_spec: code.s1("rtl(0)").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code.between("for", "end for;").token_span(),
|
||||
},
|
||||
end_token: code.s("end", 2).token(),
|
||||
end_ident_pos: None,
|
||||
}
|
||||
);
|
||||
|
|
@ -407,7 +446,9 @@ end configuration cfg;
|
|||
block_spec: code.s1("rtl(0)").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code.between("for", "end for;").token_span(),
|
||||
},
|
||||
end_token: code.s("end", 2).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -432,15 +473,21 @@ end configuration cfg;
|
|||
ident: code.s1("cfg").decl_ident(),
|
||||
entity_name: code.s1("entity_name").name(),
|
||||
decl: vec![
|
||||
ConfigurationDeclarativeItem::Use(code.s1("use lib.foo.bar;").use_clause()),
|
||||
ConfigurationDeclarativeItem::Use(code.s1("use lib2.foo.bar;").use_clause())
|
||||
code.s1("use lib.foo.bar;")
|
||||
.use_clause()
|
||||
.map_into(Declaration::Use),
|
||||
code.s1("use lib2.foo.bar;")
|
||||
.use_clause()
|
||||
.map_into(Declaration::Use)
|
||||
],
|
||||
vunit_bind_inds: Vec::new(),
|
||||
block_config: BlockConfiguration {
|
||||
block_spec: code.s1("rtl(0)").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code.between("for", "end for;").token_span(),
|
||||
},
|
||||
end_token: code.s("end", 2).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -465,17 +512,21 @@ end configuration cfg;
|
|||
context_clause: ContextClause::default(),
|
||||
ident: code.s1("cfg").decl_ident(),
|
||||
entity_name: code.s1("entity_name").name(),
|
||||
decl: vec![ConfigurationDeclarativeItem::Use(
|
||||
code.s1("use lib.foo.bar;").use_clause()
|
||||
),],
|
||||
decl: vec![code
|
||||
.s1("use lib.foo.bar;")
|
||||
.use_clause()
|
||||
.map_into(Declaration::Use)],
|
||||
vunit_bind_inds: vec![VUnitBindingIndication {
|
||||
vunit_list: vec![code.s1("baz.foobar").name()]
|
||||
vunit_list: vec![code.s1("baz.foobar").name()],
|
||||
span: code.s1("use vunit baz.foobar;").token_span()
|
||||
}],
|
||||
block_config: BlockConfiguration {
|
||||
block_spec: code.s1("rtl(0)").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code.between("for", "end for;").token_span(),
|
||||
},
|
||||
end_token: code.s("end", 2).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -504,7 +555,9 @@ end configuration cfg;
|
|||
block_spec: code.s1("rtl(0)").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code.between("for", "end for;").token_span(),
|
||||
},
|
||||
end_token: code.s("end", 2).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -541,14 +594,31 @@ end configuration cfg;
|
|||
block_spec: code.s1("name(0 to 3)").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code
|
||||
.s1("for name(0 to 3)
|
||||
end for;")
|
||||
.token_span(),
|
||||
}),
|
||||
ConfigurationItem::Block(BlockConfiguration {
|
||||
block_spec: code.s1("other_name").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code
|
||||
.s1("for other_name
|
||||
end for;")
|
||||
.token_span(),
|
||||
})
|
||||
],
|
||||
span: code
|
||||
.s1("for rtl(0)
|
||||
for name(0 to 3)
|
||||
end for;
|
||||
for other_name
|
||||
end for;
|
||||
end for;")
|
||||
.token_span(),
|
||||
},
|
||||
end_token: code.s("end", 4).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -585,7 +655,9 @@ end configuration cfg;
|
|||
instantiation_list: InstantiationList::Labels(vec![code
|
||||
.s1("inst")
|
||||
.ident()]),
|
||||
component_name: code.s1("lib.pkg.comp").name()
|
||||
colon_token: code.s1(":").token(),
|
||||
component_name: code.s1("lib.pkg.comp").name(),
|
||||
span: code.s1("for inst : lib.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: None,
|
||||
vunit_bind_inds: Vec::new(),
|
||||
|
|
@ -593,9 +665,28 @@ end configuration cfg;
|
|||
block_spec: code.s1("arch").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code
|
||||
.s1("for arch
|
||||
end for;")
|
||||
.token_span(),
|
||||
}),
|
||||
}),],
|
||||
span: code
|
||||
.s1("for inst : lib.pkg.comp
|
||||
for arch
|
||||
end for;
|
||||
end for;")
|
||||
.token_span()
|
||||
})],
|
||||
span: code
|
||||
.s1("for rtl(0)
|
||||
for inst : lib.pkg.comp
|
||||
for arch
|
||||
end for;
|
||||
end for;
|
||||
end for;")
|
||||
.token_span(),
|
||||
},
|
||||
end_token: code.s("end", 4).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -634,7 +725,9 @@ end configuration cfg;
|
|||
instantiation_list: InstantiationList::Labels(vec![code
|
||||
.s1("inst")
|
||||
.ident()]),
|
||||
component_name: code.s1("lib.pkg.comp").name()
|
||||
colon_token: code.s1(":").token(),
|
||||
component_name: code.s1("lib.pkg.comp").name(),
|
||||
span: code.s1("for inst : lib.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: Some(BindingIndication {
|
||||
entity_aspect: Some(EntityAspect::Entity(
|
||||
|
|
@ -642,18 +735,43 @@ end configuration cfg;
|
|||
None
|
||||
)),
|
||||
generic_map: None,
|
||||
port_map: None
|
||||
port_map: None,
|
||||
span: code.s1("use entity work.bar;").token_span()
|
||||
}),
|
||||
vunit_bind_inds: vec![VUnitBindingIndication {
|
||||
vunit_list: vec![code.s1("baz").name()]
|
||||
},],
|
||||
vunit_list: vec![code.s1("baz").name()],
|
||||
span: code.s1("use vunit baz;").token_span()
|
||||
}],
|
||||
block_config: Some(BlockConfiguration {
|
||||
block_spec: code.s1("arch").name(),
|
||||
use_clauses: vec![],
|
||||
items: vec![],
|
||||
span: code
|
||||
.s1("for arch
|
||||
end for;")
|
||||
.token_span(),
|
||||
}),
|
||||
}),],
|
||||
span: code
|
||||
.s1("for inst : lib.pkg.comp
|
||||
use entity work.bar;
|
||||
use vunit baz;
|
||||
for arch
|
||||
end for;
|
||||
end for;")
|
||||
.token_span(),
|
||||
})],
|
||||
span: code
|
||||
.s1("for rtl(0)
|
||||
for inst : lib.pkg.comp
|
||||
use entity work.bar;
|
||||
use vunit baz;
|
||||
for arch
|
||||
end for;
|
||||
end for;
|
||||
end for;")
|
||||
.token_span(),
|
||||
},
|
||||
end_token: code.s("end", 4).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -689,7 +807,9 @@ end configuration cfg;
|
|||
instantiation_list: InstantiationList::Labels(vec![code
|
||||
.s1("inst")
|
||||
.ident()]),
|
||||
component_name: code.s1("lib.pkg.comp").name()
|
||||
colon_token: code.s1(":").token(),
|
||||
component_name: code.s1("lib.pkg.comp").name(),
|
||||
span: code.s1("for inst : lib.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: Some(BindingIndication {
|
||||
entity_aspect: Some(EntityAspect::Entity(
|
||||
|
|
@ -698,11 +818,25 @@ end configuration cfg;
|
|||
)),
|
||||
generic_map: None,
|
||||
port_map: None,
|
||||
span: code.s1("use entity lib.use_name;").token_span()
|
||||
}),
|
||||
vunit_bind_inds: Vec::new(),
|
||||
block_config: None,
|
||||
}),],
|
||||
span: code
|
||||
.s1("for inst : lib.pkg.comp
|
||||
use entity lib.use_name;
|
||||
end for;")
|
||||
.token_span(),
|
||||
})],
|
||||
span: code
|
||||
.s1("for rtl(0)
|
||||
for inst : lib.pkg.comp
|
||||
use entity lib.use_name;
|
||||
end for;
|
||||
end for;")
|
||||
.token_span(),
|
||||
},
|
||||
end_token: code.s("end", 3).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -744,11 +878,17 @@ end configuration cfg;
|
|||
instantiation_list: InstantiationList::Labels(vec![code
|
||||
.s1("inst")
|
||||
.ident()]),
|
||||
component_name: code.s1("lib.pkg.comp").name()
|
||||
colon_token: code.s(":", 1).token(),
|
||||
component_name: code.s1("lib.pkg.comp").name(),
|
||||
span: code.s1("for inst : lib.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: None,
|
||||
vunit_bind_inds: Vec::new(),
|
||||
block_config: None,
|
||||
span: code
|
||||
.s1("for inst : lib.pkg.comp
|
||||
end for;")
|
||||
.token_span()
|
||||
}),
|
||||
ConfigurationItem::Component(ComponentConfiguration {
|
||||
spec: ComponentSpecification {
|
||||
|
|
@ -757,32 +897,65 @@ end configuration cfg;
|
|||
code.s1("inst2").ident(),
|
||||
code.s1("inst3").ident()
|
||||
]),
|
||||
component_name: code.s1("lib2.pkg.comp").name()
|
||||
colon_token: code.s(":", 2).token(),
|
||||
component_name: code.s1("lib2.pkg.comp").name(),
|
||||
span: code
|
||||
.s1("for inst1, inst2, inst3 : lib2.pkg.comp")
|
||||
.token_span()
|
||||
},
|
||||
bind_ind: None,
|
||||
vunit_bind_inds: Vec::new(),
|
||||
block_config: None,
|
||||
span: code
|
||||
.s1("for inst1, inst2, inst3 : lib2.pkg.comp
|
||||
end for;")
|
||||
.token_span()
|
||||
}),
|
||||
ConfigurationItem::Component(ComponentConfiguration {
|
||||
spec: ComponentSpecification {
|
||||
instantiation_list: InstantiationList::All,
|
||||
component_name: code.s1("lib3.pkg.comp").name()
|
||||
component_name: code.s1("lib3.pkg.comp").name(),
|
||||
colon_token: code.s(":", 3).token(),
|
||||
span: code.s1("for all : lib3.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: None,
|
||||
vunit_bind_inds: Vec::new(),
|
||||
block_config: None,
|
||||
span: code
|
||||
.s1("for all : lib3.pkg.comp
|
||||
end for;")
|
||||
.token_span()
|
||||
}),
|
||||
ConfigurationItem::Component(ComponentConfiguration {
|
||||
spec: ComponentSpecification {
|
||||
instantiation_list: InstantiationList::Others,
|
||||
component_name: code.s1("lib4.pkg.comp").name()
|
||||
component_name: code.s1("lib4.pkg.comp").name(),
|
||||
colon_token: code.s(":", 4).token(),
|
||||
span: code.s1("for others : lib4.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: None,
|
||||
vunit_bind_inds: Vec::new(),
|
||||
block_config: None,
|
||||
span: code
|
||||
.s1("for others : lib4.pkg.comp
|
||||
end for;")
|
||||
.token_span()
|
||||
})
|
||||
],
|
||||
span: code
|
||||
.s1("for rtl(0)
|
||||
for inst : lib.pkg.comp
|
||||
end for;
|
||||
for inst1, inst2, inst3 : lib2.pkg.comp
|
||||
end for;
|
||||
for all : lib3.pkg.comp
|
||||
end for;
|
||||
for others : lib4.pkg.comp
|
||||
end for;
|
||||
end for;")
|
||||
.token_span(),
|
||||
},
|
||||
end_token: code.s("end", 6).token(),
|
||||
end_ident_pos: Some(code.s("cfg", 2).token())
|
||||
}
|
||||
);
|
||||
|
|
@ -835,6 +1008,8 @@ end configuration cfg;
|
|||
spec: ComponentSpecification {
|
||||
instantiation_list: InstantiationList::All,
|
||||
component_name: code.s1("lib.pkg.comp").name(),
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.s1("for all : lib.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: BindingIndication {
|
||||
entity_aspect: Some(EntityAspect::Entity(
|
||||
|
|
@ -842,9 +1017,11 @@ end configuration cfg;
|
|||
Some(code.s1("rtl").ident())
|
||||
)),
|
||||
generic_map: None,
|
||||
port_map: None
|
||||
port_map: None,
|
||||
span: code.s1("use entity work.foo(rtl);").token_span()
|
||||
},
|
||||
vunit_bind_inds: Vec::new()
|
||||
vunit_bind_inds: Vec::new(),
|
||||
end_token: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -860,6 +1037,8 @@ end configuration cfg;
|
|||
spec: ComponentSpecification {
|
||||
instantiation_list: InstantiationList::All,
|
||||
component_name: code.s1("lib.pkg.comp").name(),
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.s1("for all : lib.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: BindingIndication {
|
||||
entity_aspect: Some(EntityAspect::Entity(
|
||||
|
|
@ -867,9 +1046,11 @@ end configuration cfg;
|
|||
Some(code.s1("rtl").ident())
|
||||
)),
|
||||
generic_map: None,
|
||||
port_map: None
|
||||
port_map: None,
|
||||
span: code.s1("use entity work.foo(rtl);").token_span()
|
||||
},
|
||||
vunit_bind_inds: Vec::new()
|
||||
vunit_bind_inds: Vec::new(),
|
||||
end_token: Some(code.s1("end").token())
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -887,6 +1068,8 @@ end configuration cfg;
|
|||
spec: ComponentSpecification {
|
||||
instantiation_list: InstantiationList::All,
|
||||
component_name: code.s1("lib.pkg.comp").name(),
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.s1("for all : lib.pkg.comp").token_span()
|
||||
},
|
||||
bind_ind: BindingIndication {
|
||||
entity_aspect: Some(EntityAspect::Entity(
|
||||
|
|
@ -894,11 +1077,14 @@ end configuration cfg;
|
|||
Some(code.s1("rtl").ident())
|
||||
)),
|
||||
generic_map: None,
|
||||
port_map: None
|
||||
port_map: None,
|
||||
span: code.s1("use entity work.foo(rtl);").token_span()
|
||||
},
|
||||
vunit_bind_inds: vec![VUnitBindingIndication {
|
||||
vunit_list: vec![code.s1("bar").name(), code.s1("baz").name()]
|
||||
vunit_list: vec![code.s1("bar").name(), code.s1("baz").name()],
|
||||
span: code.s1("use vunit bar, baz;").token_span()
|
||||
}],
|
||||
end_token: Some(code.s1("end").token()),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,15 @@
|
|||
|
||||
use super::common::check_end_identifier_mismatch;
|
||||
use super::common::ParseResult;
|
||||
use super::names::parse_name;
|
||||
use super::names::{parse_identifier_list, parse_name};
|
||||
use super::tokens::{Kind::*, TokenSpan};
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::*;
|
||||
use crate::syntax::parser::ParsingContext;
|
||||
use crate::syntax::recover;
|
||||
use crate::syntax::recover::expect_semicolon;
|
||||
use crate::syntax::separated_list::{parse_ident_list, parse_name_list};
|
||||
use crate::syntax::separated_list::parse_name_list;
|
||||
use itertools::Itertools;
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum DeclarationOrReference {
|
||||
Declaration(ContextDeclaration),
|
||||
|
|
@ -23,7 +24,10 @@ pub enum DeclarationOrReference {
|
|||
/// LRM 13. Design units and their analysis
|
||||
pub fn parse_library_clause(ctx: &mut ParsingContext<'_>) -> ParseResult<LibraryClause> {
|
||||
let library_token = ctx.stream.expect_kind(Library)?;
|
||||
let name_list = parse_ident_list(ctx)?;
|
||||
let name_list = parse_identifier_list(ctx)?
|
||||
.into_iter()
|
||||
.map(WithRef::new)
|
||||
.collect_vec();
|
||||
let semi_token = recover::expect_semicolon_or_last(ctx);
|
||||
Ok(LibraryClause {
|
||||
span: TokenSpan::new(library_token, semi_token),
|
||||
|
|
@ -64,6 +68,7 @@ pub fn parse_context(ctx: &mut ParsingContext<'_>) -> ParseResult<DeclarationOrR
|
|||
if ctx.stream.skip_if_kind(Is) {
|
||||
let mut items = Vec::with_capacity(16);
|
||||
let end_ident;
|
||||
let end_token;
|
||||
loop {
|
||||
let token = ctx.stream.peek_expect()?;
|
||||
try_init_token_kind!(
|
||||
|
|
@ -72,6 +77,7 @@ pub fn parse_context(ctx: &mut ParsingContext<'_>) -> ParseResult<DeclarationOrR
|
|||
Use => items.push(ContextItem::Use(parse_use_clause(ctx)?.item)),
|
||||
Context => items.push(ContextItem::Context(parse_context_reference(ctx)?)),
|
||||
End => {
|
||||
end_token = ctx.stream.get_current_token_id();
|
||||
ctx.stream.skip();
|
||||
ctx.stream.pop_if_kind(Context);
|
||||
end_ident = ctx.stream.pop_optional_ident();
|
||||
|
|
@ -82,26 +88,24 @@ pub fn parse_context(ctx: &mut ParsingContext<'_>) -> ParseResult<DeclarationOrR
|
|||
}
|
||||
|
||||
let ident = WithDecl::new(to_simple_name(ctx, name)?);
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
let semicolon = ctx.stream.get_last_token_id();
|
||||
Ok(DeclarationOrReference::Declaration(ContextDeclaration {
|
||||
span: TokenSpan::new(context_token, end_token),
|
||||
span: TokenSpan::new(context_token, semicolon),
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, &ident.tree, end_ident),
|
||||
ident,
|
||||
end_token,
|
||||
items,
|
||||
}))
|
||||
} else {
|
||||
// Context reference
|
||||
let mut items = vec![name];
|
||||
let mut tokens = Vec::new();
|
||||
while let Some(comma) = ctx.stream.pop_if_kind(Comma) {
|
||||
while ctx.stream.pop_if_kind(Comma).is_some() {
|
||||
items.push(parse_name(ctx)?);
|
||||
tokens.push(comma);
|
||||
}
|
||||
let name_list = SeparatedList { items, tokens };
|
||||
let semi_token = recover::expect_semicolon_or_last(ctx);
|
||||
Ok(DeclarationOrReference::Reference(ContextReference {
|
||||
span: TokenSpan::new(context_token, semi_token),
|
||||
name_list,
|
||||
name_list: items,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +126,7 @@ mod tests {
|
|||
code.with_stream_no_diagnostics(parse_library_clause),
|
||||
LibraryClause {
|
||||
span: code.token_span(),
|
||||
name_list: code.s1("foo").ident_list(),
|
||||
name_list: vec![code.s1("foo").ident().into_ref()],
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -134,7 +138,10 @@ mod tests {
|
|||
code.with_stream_no_diagnostics(parse_library_clause),
|
||||
LibraryClause {
|
||||
span: code.token_span(),
|
||||
name_list: code.s1("foo, bar").ident_list(),
|
||||
name_list: vec![
|
||||
code.s1("foo").ident().into_ref(),
|
||||
code.s1("bar").ident().into_ref()
|
||||
],
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -147,7 +154,7 @@ mod tests {
|
|||
WithTokenSpan::new(
|
||||
UseClause {
|
||||
span: code.token_span(),
|
||||
name_list: code.s1("lib.foo").name_list(),
|
||||
name_list: vec![code.s1("lib.foo").name()],
|
||||
},
|
||||
code.token_span()
|
||||
),
|
||||
|
|
@ -162,7 +169,7 @@ mod tests {
|
|||
WithTokenSpan::new(
|
||||
UseClause {
|
||||
span: code.token_span(),
|
||||
name_list: code.s1("foo.'a', lib.bar.all").name_list(),
|
||||
name_list: vec![code.s1("foo.'a'").name(), code.s1("lib.bar.all").name()],
|
||||
},
|
||||
code.token_span()
|
||||
),
|
||||
|
|
@ -176,8 +183,8 @@ mod tests {
|
|||
code.with_stream_no_diagnostics(parse_context),
|
||||
DeclarationOrReference::Reference(ContextReference {
|
||||
span: code.token_span(),
|
||||
name_list: code.s1("lib.foo").name_list(),
|
||||
},)
|
||||
name_list: vec![code.s1("lib.foo").name()],
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +217,7 @@ end context ident;
|
|||
span: code.token_span(),
|
||||
ident: code.s1("ident").decl_ident(),
|
||||
items: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: if has_end_ident {
|
||||
Some(code.s("ident", 2).token())
|
||||
} else {
|
||||
|
|
@ -242,6 +250,7 @@ end context ident2;
|
|||
span: code.token_span(),
|
||||
ident: code.s1("ident").decl_ident(),
|
||||
items: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
})
|
||||
);
|
||||
|
|
@ -266,17 +275,18 @@ end context;
|
|||
items: vec![
|
||||
ContextItem::Library(LibraryClause {
|
||||
span: TokenSpan::new(code.s("library", 1).token(), code.s(";", 1).token()),
|
||||
name_list: code.s1("foo").ident_list(),
|
||||
name_list: vec![code.s1("foo").ident().into_ref()],
|
||||
}),
|
||||
ContextItem::Use(UseClause {
|
||||
span: code.s1("use foo.bar;").token_span(),
|
||||
name_list: code.s1("foo.bar").name_list(),
|
||||
name_list: vec![code.s1("foo.bar").name()],
|
||||
}),
|
||||
ContextItem::Context(ContextReference {
|
||||
span: TokenSpan::new(code.s("context", 2).token(), code.s(";", 3).token()),
|
||||
name_list: code.s1("foo.ctx").name_list(),
|
||||
name_list: vec![code.s1("foo.ctx").name()],
|
||||
}),
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -120,12 +120,8 @@ pub fn parse_declarative_part(
|
|||
|
||||
File | Shared | Constant | Signal | Variable | Attribute => {
|
||||
let decls: ParseResult<Vec<WithTokenSpan<Declaration>>> = match token.kind {
|
||||
File => parse_file_declaration(ctx).map(|decls| {
|
||||
decls
|
||||
.into_iter()
|
||||
.map(|decl| decl.map_into(Declaration::File))
|
||||
.collect()
|
||||
}),
|
||||
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| {
|
||||
|
|
@ -263,6 +259,7 @@ constant x: natural := 5;
|
|||
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())
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -34,23 +34,26 @@ pub fn parse_entity_declaration(ctx: &mut ParsingContext<'_>) -> ParseResult<Ent
|
|||
|
||||
let decl = parse_declarative_part(ctx)?;
|
||||
|
||||
let statements = if ctx.stream.skip_if_kind(Begin) {
|
||||
let begin_token = ctx.stream.pop_if_kind(Begin);
|
||||
let statements = if begin_token.is_some() {
|
||||
parse_labeled_concurrent_statements(ctx)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
ctx.stream.pop_if_kind(End);
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.pop_if_kind(Entity);
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let semicolon_token = expect_semicolon_or_last(ctx);
|
||||
Ok(EntityDeclaration {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, semicolon_token),
|
||||
context_clause: ContextClause::default(),
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, &ident.tree, end_ident),
|
||||
ident,
|
||||
generic_clause,
|
||||
port_clause,
|
||||
begin_token,
|
||||
decl,
|
||||
end_token,
|
||||
statements,
|
||||
})
|
||||
}
|
||||
|
|
@ -67,20 +70,21 @@ pub fn parse_architecture_body(ctx: &mut ParsingContext<'_>) -> ParseResult<Arch
|
|||
let begin_token = ctx.stream.expect_kind(Begin)?;
|
||||
|
||||
let statements = parse_labeled_concurrent_statements(ctx)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.pop_if_kind(Architecture);
|
||||
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let semicolon_token = expect_semicolon_or_last(ctx);
|
||||
|
||||
Ok(ArchitectureBody {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, semicolon_token),
|
||||
context_clause: ContextClause::default(),
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, &ident.tree, end_ident),
|
||||
begin_token,
|
||||
ident,
|
||||
entity_name: entity_name.into_ref(),
|
||||
decl,
|
||||
end_token,
|
||||
statements,
|
||||
})
|
||||
}
|
||||
|
|
@ -93,15 +97,16 @@ pub fn parse_package_declaration(ctx: &mut ParsingContext<'_>) -> ParseResult<Pa
|
|||
ctx.stream.expect_kind(Is)?;
|
||||
let generic_clause = parse_optional_generic_list(ctx)?;
|
||||
let decl = parse_declarative_part(ctx)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.pop_if_kind(Package);
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let semicolon = expect_semicolon_or_last(ctx);
|
||||
Ok(PackageDeclaration {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, semicolon),
|
||||
context_clause: ContextClause::default(),
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, &ident.tree, end_ident),
|
||||
ident,
|
||||
end_token,
|
||||
generic_clause,
|
||||
decl,
|
||||
})
|
||||
|
|
@ -115,18 +120,19 @@ pub fn parse_package_body(ctx: &mut ParsingContext<'_>) -> ParseResult<PackageBo
|
|||
|
||||
ctx.stream.expect_kind(Is)?;
|
||||
let decl = parse_declarative_part(ctx)?;
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
if ctx.stream.skip_if_kind(Package) {
|
||||
ctx.stream.expect_kind(Body)?;
|
||||
}
|
||||
let end_ident = ctx.stream.pop_optional_ident();
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let semicolon_token = expect_semicolon_or_last(ctx);
|
||||
|
||||
Ok(PackageBody {
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
span: TokenSpan::new(start_token, semicolon_token),
|
||||
context_clause: ContextClause::default(),
|
||||
decl,
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, &ident, end_ident),
|
||||
end_token,
|
||||
ident: ident.into(),
|
||||
})
|
||||
}
|
||||
|
|
@ -301,6 +307,7 @@ mod tests {
|
|||
ident: Ident,
|
||||
span: TokenSpan,
|
||||
end_ident_pos: Option<TokenId>,
|
||||
end_token: TokenId,
|
||||
) -> AnyDesignUnit {
|
||||
AnyDesignUnit::Primary(AnyPrimaryUnit::Entity(EntityDeclaration {
|
||||
span,
|
||||
|
|
@ -310,6 +317,8 @@ mod tests {
|
|||
port_clause: None,
|
||||
decl: vec![],
|
||||
statements: vec![],
|
||||
begin_token: None,
|
||||
end_token,
|
||||
end_ident_pos,
|
||||
}))
|
||||
}
|
||||
|
|
@ -326,7 +335,12 @@ end entity;
|
|||
design_file.design_units,
|
||||
[(
|
||||
code.tokenize(),
|
||||
simple_entity(code.s1("myent").ident(), code.token_span(), None)
|
||||
simple_entity(
|
||||
code.s1("myent").ident(),
|
||||
code.token_span(),
|
||||
None,
|
||||
code.s1("end").token()
|
||||
)
|
||||
)]
|
||||
);
|
||||
|
||||
|
|
@ -344,6 +358,7 @@ end entity myent;
|
|||
code.s1("myent").ident(),
|
||||
code.token_span(),
|
||||
Some(code.s("myent", 2).token()),
|
||||
code.s1("end").token()
|
||||
)
|
||||
)]
|
||||
);
|
||||
|
|
@ -380,6 +395,8 @@ end entity;
|
|||
decl: vec![],
|
||||
statements: vec![],
|
||||
end_ident_pos: None,
|
||||
begin_token: None,
|
||||
end_token: code.s1("end").token()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -410,6 +427,8 @@ end entity;
|
|||
decl: vec![],
|
||||
statements: vec![],
|
||||
end_ident_pos: None,
|
||||
begin_token: None,
|
||||
end_token: code.s1("end").token()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -445,6 +464,8 @@ end entity;
|
|||
decl: vec![],
|
||||
statements: vec![],
|
||||
end_ident_pos: None,
|
||||
begin_token: None,
|
||||
end_token: code.s1("end").token()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -469,6 +490,8 @@ end entity;
|
|||
decl: vec![],
|
||||
statements: vec![],
|
||||
end_ident_pos: None,
|
||||
begin_token: Some(code.s1("begin").token()),
|
||||
end_token: code.s1("end").token()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -493,6 +516,8 @@ end entity;
|
|||
decl: code.s1("constant foo : natural := 0;").declarative_part(),
|
||||
statements: vec![],
|
||||
end_ident_pos: None,
|
||||
begin_token: None,
|
||||
end_token: code.s1("end").token()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -515,9 +540,11 @@ end entity;
|
|||
ident: code.s1("myent").decl_ident(),
|
||||
generic_clause: None,
|
||||
port_clause: None,
|
||||
begin_token: Some(code.s1("begin").token()),
|
||||
decl: vec![],
|
||||
statements: vec![code.s1("check(clk, valid);").concurrent_statement()],
|
||||
end_ident_pos: None,
|
||||
end_token: code.s1("end").token()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -573,6 +600,7 @@ end;
|
|||
code_myent.s1("myent").ident(),
|
||||
code_myent.token_span(),
|
||||
None,
|
||||
code_myent.s1("end").token()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
@ -584,6 +612,7 @@ end;
|
|||
code_myent2.s1("myent2").ident(),
|
||||
code_myent2.token_span(),
|
||||
Some(code_myent2.s("myent2", 2).token()),
|
||||
code_myent2.s1("end").token()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
@ -595,6 +624,7 @@ end;
|
|||
code_myent3.s1("myent3").ident(),
|
||||
code_myent3.token_span(),
|
||||
Some(code_myent3.s("myent3", 2).token()),
|
||||
code_myent3.s1("end").token()
|
||||
)
|
||||
),
|
||||
);
|
||||
|
|
@ -605,7 +635,8 @@ end;
|
|||
simple_entity(
|
||||
code_myent4.s1("myent4").ident(),
|
||||
code_myent4.token_span(),
|
||||
None
|
||||
None,
|
||||
code_myent4.s1("end").token()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
@ -619,6 +650,7 @@ end;
|
|||
span: TokenSpan,
|
||||
begin_token: TokenId,
|
||||
end_ident_pos: Option<TokenId>,
|
||||
end_token: TokenId,
|
||||
) -> AnyDesignUnit {
|
||||
AnyDesignUnit::Secondary(AnySecondaryUnit::Architecture(ArchitectureBody {
|
||||
span,
|
||||
|
|
@ -628,6 +660,7 @@ end;
|
|||
entity_name: entity_name.into_ref(),
|
||||
decl: Vec::new(),
|
||||
statements: vec![],
|
||||
end_token,
|
||||
end_ident_pos,
|
||||
}))
|
||||
}
|
||||
|
|
@ -651,6 +684,7 @@ end architecture;
|
|||
code.token_span(),
|
||||
code.s1("begin").token(),
|
||||
None,
|
||||
code.s1("end").token(),
|
||||
)
|
||||
)]
|
||||
);
|
||||
|
|
@ -675,6 +709,7 @@ end architecture arch_name;
|
|||
code.token_span(),
|
||||
code.s1("begin").token(),
|
||||
Some(code.s("arch_name", 2).token()),
|
||||
code.s1("end").token(),
|
||||
)
|
||||
)]
|
||||
);
|
||||
|
|
@ -699,6 +734,7 @@ end;
|
|||
code.token_span(),
|
||||
code.s1("begin").token(),
|
||||
None,
|
||||
code.s1("end").token(),
|
||||
)
|
||||
)]
|
||||
);
|
||||
|
|
@ -720,6 +756,7 @@ end package;
|
|||
ident: code.s1("pkg_name").decl_ident(),
|
||||
generic_clause: None,
|
||||
decl: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
}
|
||||
);
|
||||
|
|
@ -748,6 +785,7 @@ end package;
|
|||
constant bar : natural := 0;
|
||||
")
|
||||
.declarative_part(),
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
}
|
||||
);
|
||||
|
|
@ -777,6 +815,7 @@ end package;
|
|||
span: code.between("generic (", ");").token_span()
|
||||
}),
|
||||
decl: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
}
|
||||
);
|
||||
|
|
@ -802,14 +841,16 @@ end entity;
|
|||
span: code.s1_to_end("entity").token_span(),
|
||||
context_clause: vec![
|
||||
ContextItem::Library(code.s1("library lib;").library_clause()),
|
||||
ContextItem::Use(code.s1("use lib.foo;").use_clause()),
|
||||
ContextItem::Use(code.s1("use lib.foo;").use_clause().item),
|
||||
],
|
||||
ident: code.s1("myent").decl_ident(),
|
||||
generic_clause: None,
|
||||
port_clause: None,
|
||||
decl: vec![],
|
||||
begin_token: None,
|
||||
statements: vec![],
|
||||
end_ident_pos: None,
|
||||
end_token: code.s1("end").token()
|
||||
}))
|
||||
)]
|
||||
}
|
||||
|
|
@ -927,14 +968,14 @@ end entity y;
|
|||
let (tokens, unit) = &file.design_units[0];
|
||||
let ent = unit.expect_entity();
|
||||
let lib = ent.context_clause[0].expect_library_clause();
|
||||
let tok = tokens.get_token(lib.get_start_token());
|
||||
let tok = tokens.index(lib.get_start_token());
|
||||
assert_eq!(tok.kind, Library);
|
||||
assert_eq!(tok.pos, code.s1("library").pos());
|
||||
|
||||
let (tokens, unit) = &file.design_units[2];
|
||||
let ent = unit.expect_entity();
|
||||
let ctx_ref = ent.context_clause[0].expect_context_reference();
|
||||
let tok = tokens.get_token(ctx_ref.get_start_token());
|
||||
let tok = tokens.index(ctx_ref.get_start_token());
|
||||
assert_eq!(tok.kind, Context);
|
||||
assert_eq!(tok.pos, code.s1("context").pos());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::ast::token_range::{WithToken, WithTokenSpan};
|
|||
use crate::ast::{Literal, *};
|
||||
use crate::data::Diagnostic;
|
||||
use crate::syntax::TokenAccess;
|
||||
use crate::{ast, TokenSpan};
|
||||
use crate::{ast, HasTokenSpan, TokenId, TokenSpan};
|
||||
use vhdl_lang::syntax::parser::ParsingContext;
|
||||
|
||||
impl WithTokenSpan<Name> {
|
||||
|
|
@ -145,8 +145,9 @@ fn kind_to_binary_op(kind: Kind) -> Option<(Operator, usize)> {
|
|||
|
||||
pub fn parse_aggregate_initial_choices(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
start_token: TokenId,
|
||||
choices: Vec<WithTokenSpan<Choice>>,
|
||||
) -> ParseResult<WithTokenSpan<Vec<ElementAssociation>>> {
|
||||
) -> ParseResult<WithTokenSpan<Vec<WithTokenSpan<ElementAssociation>>>> {
|
||||
let mut choices = choices;
|
||||
let mut result = Vec::new();
|
||||
loop {
|
||||
|
|
@ -157,8 +158,13 @@ pub fn parse_aggregate_initial_choices(
|
|||
RightPar => {
|
||||
if choices.len() == 1 {
|
||||
if let Some(WithTokenSpan{item: Choice::Expression(expr), span}) = choices.pop() {
|
||||
result.push(ElementAssociation::Positional(WithTokenSpan::new(expr, span)));
|
||||
return Ok(WithTokenSpan::from(result, token_id))
|
||||
result.push(
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Positional(WithTokenSpan::new(expr, span)),
|
||||
span,
|
||||
)
|
||||
);
|
||||
return Ok(WithTokenSpan::new(result, TokenSpan::new(start_token, token_id)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +173,12 @@ pub fn parse_aggregate_initial_choices(
|
|||
Comma => {
|
||||
if choices.len() == 1 {
|
||||
if let Some(WithTokenSpan{item: Choice::Expression(expr), span}) = choices.pop() {
|
||||
result.push(ElementAssociation::Positional(WithTokenSpan::new(expr, span)));
|
||||
result.push(
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Positional(WithTokenSpan::new(expr, span)),
|
||||
span
|
||||
)
|
||||
);
|
||||
choices = parse_choices(ctx)?;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -177,14 +188,20 @@ pub fn parse_aggregate_initial_choices(
|
|||
},
|
||||
RightArrow => {
|
||||
let rhs = parse_expression(ctx)?;
|
||||
result.push(ElementAssociation::Named(choices, rhs));
|
||||
let span = TokenSpan::new(choices[0].get_start_token(), rhs.get_end_token());
|
||||
result.push(
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Named(choices, rhs),
|
||||
span,
|
||||
)
|
||||
);
|
||||
|
||||
expect_token!(
|
||||
ctx.stream,
|
||||
token,
|
||||
token_id,
|
||||
RightPar => {
|
||||
return Ok(WithTokenSpan::from(result, token_id))
|
||||
return Ok(WithTokenSpan::new(result, TokenSpan::new(start_token, token_id)))
|
||||
},
|
||||
Comma => {
|
||||
choices = parse_choices(ctx)?;
|
||||
|
|
@ -197,7 +214,7 @@ pub fn parse_aggregate_initial_choices(
|
|||
|
||||
pub fn parse_aggregate(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
) -> ParseResult<WithTokenSpan<Vec<ElementAssociation>>> {
|
||||
) -> ParseResult<WithTokenSpan<Vec<WithTokenSpan<ElementAssociation>>>> {
|
||||
let start_tok = ctx.stream.expect_kind(LeftPar)?;
|
||||
if let Some(token) = ctx.stream.pop_if_kind(RightPar) {
|
||||
return Ok(WithTokenSpan::from(
|
||||
|
|
@ -206,7 +223,7 @@ pub fn parse_aggregate(
|
|||
));
|
||||
};
|
||||
let choices = parse_choices(ctx)?;
|
||||
parse_aggregate_initial_choices(ctx, choices)
|
||||
parse_aggregate_initial_choices(ctx, start_tok, choices)
|
||||
}
|
||||
|
||||
fn parse_half_range(
|
||||
|
|
@ -280,7 +297,7 @@ fn parse_allocator(ctx: &mut ParsingContext<'_>) -> ParseResult<WithTokenSpan<Al
|
|||
};
|
||||
|
||||
let subtype = SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark,
|
||||
constraint,
|
||||
};
|
||||
|
|
@ -331,6 +348,7 @@ fn name_to_selected_name(name: Name) -> Option<Name> {
|
|||
|
||||
fn parse_expression_or_aggregate(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
start_token: TokenId,
|
||||
) -> ParseResult<WithTokenSpan<Expression>> {
|
||||
let mut choices = parse_choices(ctx)?;
|
||||
|
||||
|
|
@ -358,6 +376,7 @@ fn parse_expression_or_aggregate(
|
|||
Comma | RightArrow => {
|
||||
Ok(parse_aggregate_initial_choices(
|
||||
ctx,
|
||||
start_token,
|
||||
vec![WithTokenSpan::new(Choice::Expression(expr), span)],
|
||||
)?.map_into(Expression::Aggregate))
|
||||
},
|
||||
|
|
@ -365,17 +384,14 @@ fn parse_expression_or_aggregate(
|
|||
// Was expression with parenthesis
|
||||
RightPar => {
|
||||
ctx.stream.skip();
|
||||
// Lexical position between parenthesis
|
||||
let expr = WithTokenSpan::from(
|
||||
expr,
|
||||
token_id
|
||||
);
|
||||
let expr = WithTokenSpan::new(Expression::Parenthesized(Box::new(WithTokenSpan::new(expr, span))), TokenSpan::new(start_token, token_id));
|
||||
Ok(expr)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// Must be aggregate
|
||||
Ok(parse_aggregate_initial_choices(ctx, choices)?.map_into(Expression::Aggregate))
|
||||
Ok(parse_aggregate_initial_choices(ctx, start_token, choices)?
|
||||
.map_into(Expression::Aggregate))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -391,7 +407,7 @@ fn parse_primary(ctx: &mut ParsingContext<'_>) -> ParseResult<WithTokenSpan<Expr
|
|||
let name = parse_name(ctx)?;
|
||||
if ctx.stream.skip_if_kind(Tick) {
|
||||
let lpar = ctx.stream.expect_kind(LeftPar)?;
|
||||
let expr = parse_expression_or_aggregate(ctx)?.start_with(lpar);
|
||||
let expr = parse_expression_or_aggregate(ctx, lpar)?;
|
||||
let span = name.span.combine(expr.span);
|
||||
Ok(WithTokenSpan::new(
|
||||
Expression::Qualified(Box::new(QualifiedExpression {
|
||||
|
|
@ -450,7 +466,7 @@ fn parse_primary(ctx: &mut ParsingContext<'_>) -> ParseResult<WithTokenSpan<Expr
|
|||
if let Some(unit_token) = ctx.stream.pop_if_kind(Identifier) {
|
||||
let unit = ctx
|
||||
.stream
|
||||
.get_token(unit_token)
|
||||
.index(unit_token)
|
||||
.to_identifier_value(unit_token)?;
|
||||
let span = TokenSpan::new(value.token, unit.token);
|
||||
let physical = PhysicalLiteral {
|
||||
|
|
@ -468,8 +484,9 @@ fn parse_primary(ctx: &mut ParsingContext<'_>) -> ParseResult<WithTokenSpan<Expr
|
|||
}
|
||||
|
||||
LeftPar => {
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
ctx.stream.skip();
|
||||
parse_expression_or_aggregate(ctx).map(|expr| expr.start_with(token_id))
|
||||
parse_expression_or_aggregate(ctx, start_token)
|
||||
}
|
||||
|
||||
kind => {
|
||||
|
|
@ -959,8 +976,14 @@ mod tests {
|
|||
};
|
||||
|
||||
let assoc_list = vec![
|
||||
ElementAssociation::Positional(one_expr),
|
||||
ElementAssociation::Positional(two_expr),
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Positional(one_expr),
|
||||
code.s1("1").token_span(),
|
||||
),
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Positional(two_expr),
|
||||
code.s1("2").token_span(),
|
||||
),
|
||||
];
|
||||
let expr = WithTokenSpan {
|
||||
item: Expression::Aggregate(assoc_list),
|
||||
|
|
@ -982,9 +1005,9 @@ mod tests {
|
|||
span: code.s1("2").token_span(),
|
||||
};
|
||||
|
||||
let assoc_list = vec![ElementAssociation::Named(
|
||||
vec![one_expr.map_into(Choice::Expression)],
|
||||
two_expr,
|
||||
let assoc_list = vec![WithTokenSpan::new(
|
||||
ElementAssociation::Named(vec![one_expr.map_into(Choice::Expression)], two_expr),
|
||||
code.s1("1 => 2").token_span(),
|
||||
)];
|
||||
let expr = WithTokenSpan {
|
||||
item: Expression::Aggregate(assoc_list),
|
||||
|
|
@ -1012,12 +1035,15 @@ mod tests {
|
|||
span: code.s1("3").token_span(),
|
||||
};
|
||||
|
||||
let assoc_list = vec![ElementAssociation::Named(
|
||||
vec![
|
||||
one_expr.map_into(Choice::Expression),
|
||||
two_expr.map_into(Choice::Expression),
|
||||
],
|
||||
three_expr,
|
||||
let assoc_list = vec![WithTokenSpan::new(
|
||||
ElementAssociation::Named(
|
||||
vec![
|
||||
one_expr.map_into(Choice::Expression),
|
||||
two_expr.map_into(Choice::Expression),
|
||||
],
|
||||
three_expr,
|
||||
),
|
||||
code.s1("1 | 2 => 3").token_span(),
|
||||
)];
|
||||
let expr = WithTokenSpan {
|
||||
item: Expression::Aggregate(assoc_list),
|
||||
|
|
@ -1035,12 +1061,15 @@ mod tests {
|
|||
span: code.s1("1").token_span(),
|
||||
};
|
||||
|
||||
let assoc_list = vec![ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(
|
||||
Choice::Others,
|
||||
code.s1("others").token_span(),
|
||||
)],
|
||||
one_expr,
|
||||
let assoc_list = vec![WithTokenSpan::new(
|
||||
ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(
|
||||
Choice::Others,
|
||||
code.s1("others").token_span(),
|
||||
)],
|
||||
one_expr,
|
||||
),
|
||||
code.s1("others => 1").token_span(),
|
||||
)];
|
||||
let expr = WithTokenSpan {
|
||||
item: Expression::Aggregate(assoc_list),
|
||||
|
|
@ -1084,9 +1113,16 @@ mod tests {
|
|||
right_expr: Box::new(zero_expr),
|
||||
}));
|
||||
|
||||
let assoc_list = vec![ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(Choice::DiscreteRange(range), pos)],
|
||||
two_expr,
|
||||
let assoc_list = vec![WithTokenSpan::new(
|
||||
ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(Choice::DiscreteRange(range), pos)],
|
||||
two_expr,
|
||||
),
|
||||
if *direction == Direction::Descending {
|
||||
code.s1("1 downto 0 => 2").token_span()
|
||||
} else {
|
||||
code.s1("1 to 0 => 2").token_span()
|
||||
},
|
||||
)];
|
||||
let expr = WithTokenSpan {
|
||||
item: Expression::Aggregate(assoc_list),
|
||||
|
|
@ -1111,19 +1147,25 @@ mod tests {
|
|||
};
|
||||
|
||||
let assoc_list = vec![
|
||||
ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(
|
||||
Choice::Others,
|
||||
code.s("others", 1).token_span(),
|
||||
)],
|
||||
one_expr,
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(
|
||||
Choice::Others,
|
||||
code.s("others", 1).token_span(),
|
||||
)],
|
||||
one_expr,
|
||||
),
|
||||
code.s1("others => 1").token_span(),
|
||||
),
|
||||
ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(
|
||||
Choice::Others,
|
||||
code.s("others", 2).token_span(),
|
||||
)],
|
||||
two_expr,
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Named(
|
||||
vec![WithTokenSpan::new(
|
||||
Choice::Others,
|
||||
code.s("others", 2).token_span(),
|
||||
)],
|
||||
two_expr,
|
||||
),
|
||||
code.s1("others => 2").token_span(),
|
||||
),
|
||||
];
|
||||
let expr = WithTokenSpan {
|
||||
|
|
@ -1151,8 +1193,14 @@ mod tests {
|
|||
};
|
||||
|
||||
let assoc_list = vec![
|
||||
ElementAssociation::Named(vec![one_expr.map_into(Choice::Expression)], two_expr),
|
||||
ElementAssociation::Positional(three_expr),
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Named(vec![one_expr.map_into(Choice::Expression)], two_expr),
|
||||
code.s1("1 => 2").token_span(),
|
||||
),
|
||||
WithTokenSpan::new(
|
||||
ElementAssociation::Positional(three_expr),
|
||||
code.s1("3").token_span(),
|
||||
),
|
||||
];
|
||||
let expr = WithTokenSpan {
|
||||
item: Expression::Aggregate(assoc_list),
|
||||
|
|
@ -1199,14 +1247,19 @@ mod tests {
|
|||
Box::new(two),
|
||||
Box::new(three),
|
||||
),
|
||||
span: code.s1("(2 + 3)").token_span(),
|
||||
span: code.s1("2 + 3").token_span(),
|
||||
};
|
||||
|
||||
let expr_add0_paren = WithTokenSpan::new(
|
||||
Expression::Parenthesized(Box::new(expr_add0)),
|
||||
code.s1("(2 + 3)").token_span(),
|
||||
);
|
||||
|
||||
let expr_add1 = WithTokenSpan {
|
||||
item: Expression::Binary(
|
||||
WithToken::new(WithRef::new(Operator::Plus), code.s("+", 1).token()),
|
||||
Box::new(one),
|
||||
Box::new(expr_add0),
|
||||
Box::new(expr_add0_paren),
|
||||
),
|
||||
span: code.token_span(),
|
||||
};
|
||||
|
|
@ -1239,13 +1292,18 @@ mod tests {
|
|||
Box::new(one),
|
||||
Box::new(two),
|
||||
),
|
||||
span: code.s1("(1 + 2)").token_span(),
|
||||
span: code.s1("1 + 2").token_span(),
|
||||
};
|
||||
|
||||
let expr_add0_paren = WithTokenSpan::new(
|
||||
Expression::Parenthesized(Box::new(expr_add0)),
|
||||
code.s1("(1 + 2)").token_span(),
|
||||
);
|
||||
|
||||
let expr_add1 = WithTokenSpan {
|
||||
item: Expression::Binary(
|
||||
WithToken::new(WithRef::new(Operator::Plus), code.s("+", 2).token()),
|
||||
Box::new(expr_add0),
|
||||
Box::new(expr_add0_paren),
|
||||
Box::new(three),
|
||||
),
|
||||
span: code.token_span(),
|
||||
|
|
@ -1283,6 +1341,9 @@ mod tests {
|
|||
panic!("Cannot format {lit:?}");
|
||||
}
|
||||
},
|
||||
Expression::Parenthesized(ref expr) => {
|
||||
format!("({})", fmt(ctx, expr))
|
||||
}
|
||||
_ => {
|
||||
println!("{}", expr.pos(ctx).code_context());
|
||||
panic!("Cannot format {expr:?}");
|
||||
|
|
@ -1302,7 +1363,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_partial_stream(parse_expression),
|
||||
Err(Diagnostic::syntax_error(
|
||||
&code.s1(",").pos(),
|
||||
code.s1(",").pos(),
|
||||
"Expected {expression}"
|
||||
))
|
||||
);
|
||||
|
|
@ -1311,7 +1372,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_partial_stream(parse_expression),
|
||||
Err(Diagnostic::syntax_error(
|
||||
&code.s1(")").pos(),
|
||||
code.s1(")").pos(),
|
||||
"Expected {expression}"
|
||||
))
|
||||
);
|
||||
|
|
@ -1319,7 +1380,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_partial_stream(parse_expression),
|
||||
Err(Diagnostic::syntax_error(
|
||||
&code.s(",", 2).pos(),
|
||||
code.s(",", 2).pos(),
|
||||
"Expected {expression}"
|
||||
))
|
||||
);
|
||||
|
|
@ -1340,7 +1401,11 @@ mod tests {
|
|||
|
||||
assert_expression_is("1+2*3", "(Integer(1) Plus (Integer(2) Times Integer(3)))");
|
||||
|
||||
assert_expression_is("(1+2)*3", "((Integer(1) Plus Integer(2)) Times Integer(3))");
|
||||
// The extra parenthesis signify the user-supplied parenthesis
|
||||
assert_expression_is(
|
||||
"(1+2)*3",
|
||||
"(((Integer(1) Plus Integer(2))) Times Integer(3))",
|
||||
);
|
||||
|
||||
// Multiplication has precedence over negation.
|
||||
assert_expression_is("-1 * 2", "(Minus (Integer(1) Times Integer(2)))");
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use super::object_declaration::parse_optional_assignment;
|
|||
use super::subprogram::parse_subprogram_specification;
|
||||
use super::subtype_indication::parse_subtype_indication;
|
||||
use super::tokens::{Kind::*, *};
|
||||
use crate::ast::token_range::WithToken;
|
||||
use crate::ast::token_range::{WithToken, WithTokenSpan};
|
||||
/// LRM 6.5 Interface declarations
|
||||
use crate::ast::*;
|
||||
use crate::data::*;
|
||||
|
|
@ -71,7 +71,7 @@ fn parse_interface_file_declaration(
|
|||
.into_iter()
|
||||
.map(WithDecl::new)
|
||||
.collect_vec();
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let subtype = parse_subtype_indication(ctx)?;
|
||||
|
||||
if ctx.stream.next_kind_is(Open) {
|
||||
|
|
@ -96,6 +96,7 @@ fn parse_interface_file_declaration(
|
|||
Ok(InterfaceDeclaration::File(InterfaceFileDeclaration {
|
||||
idents,
|
||||
subtype_indication: subtype.clone(),
|
||||
colon_token,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
}))
|
||||
}
|
||||
|
|
@ -112,7 +113,7 @@ fn parse_interface_object_declaration(
|
|||
.map(WithDecl::new)
|
||||
.collect_vec();
|
||||
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let mode = if ctx.stream.next_kind_is(View) {
|
||||
ModeIndication::View(parse_view_mode_indication(ctx)?)
|
||||
} else {
|
||||
|
|
@ -129,12 +130,13 @@ fn parse_interface_object_declaration(
|
|||
list_type,
|
||||
mode: mode.clone(),
|
||||
idents,
|
||||
colon_token,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_view_mode_indication(ctx: &mut ParsingContext<'_>) -> ParseResult<ModeViewIndication> {
|
||||
ctx.stream.expect_kind(View)?;
|
||||
let start_token = ctx.stream.expect_kind(View)?;
|
||||
let (name, kind) = if ctx.stream.pop_if_kind(LeftPar).is_some() {
|
||||
let _name = parse_name(ctx)?;
|
||||
ctx.stream.expect_kind(RightPar)?;
|
||||
|
|
@ -142,15 +144,17 @@ fn parse_view_mode_indication(ctx: &mut ParsingContext<'_>) -> ParseResult<ModeV
|
|||
} else {
|
||||
(parse_name(ctx)?, ModeViewIndicationKind::Record)
|
||||
};
|
||||
let subtype_indication = if ctx.stream.pop_if_kind(Of).is_some() {
|
||||
Some(parse_subtype_indication(ctx)?)
|
||||
let subtype_indication = if let Some(of_token) = ctx.stream.pop_if_kind(Of) {
|
||||
Some((of_token, parse_subtype_indication(ctx)?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
Ok(ModeViewIndication {
|
||||
subtype_indication,
|
||||
name,
|
||||
kind,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +167,7 @@ fn parse_simple_mode_indication(
|
|||
let object_class_tok = explicit_object_class.map(|class| class.token);
|
||||
let mode_with_pos = parse_optional_mode(ctx)?;
|
||||
let mode = mode_with_pos.as_ref().map(|mode| mode.item);
|
||||
let mode_tok = mode_with_pos.map(|mode| mode.token);
|
||||
let mode_tok = mode_with_pos.as_ref().map(|mode| mode.token);
|
||||
|
||||
let object_class = match (
|
||||
list_type,
|
||||
|
|
@ -209,7 +213,7 @@ fn parse_simple_mode_indication(
|
|||
}
|
||||
|
||||
Ok(SimpleModeIndication {
|
||||
mode,
|
||||
mode: mode_with_pos,
|
||||
class: object_class,
|
||||
subtype_indication: subtype,
|
||||
expression: expr,
|
||||
|
|
@ -246,7 +250,7 @@ fn parse_interface_package(
|
|||
ctx.stream.expect_kind(Is)?;
|
||||
ctx.stream.expect_kind(New)?;
|
||||
let package_name = parse_selected_name(ctx)?;
|
||||
ctx.stream.expect_kind(Generic)?;
|
||||
let generic_token = ctx.stream.expect_kind(Generic)?;
|
||||
ctx.stream.expect_kind(Map)?;
|
||||
|
||||
let generic_map = {
|
||||
|
|
@ -274,7 +278,7 @@ fn parse_interface_package(
|
|||
Ok(InterfacePackageDeclaration {
|
||||
ident: ident.into(),
|
||||
package_name,
|
||||
generic_map,
|
||||
generic_map: WithTokenSpan::new(generic_map, TokenSpan::new(generic_token, last_token)),
|
||||
span: TokenSpan::new(start_token, last_token),
|
||||
})
|
||||
}
|
||||
|
|
@ -479,6 +483,7 @@ mod tests {
|
|||
subtype_indication: code.s1("natural").subtype_indication(),
|
||||
expression: None
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
idents: vec![code.s1("foo").decl_ident(), code.s1("bar").decl_ident()],
|
||||
span: code.between("constant", "natural").token_span()
|
||||
})],
|
||||
|
|
@ -502,6 +507,7 @@ mod tests {
|
|||
subtype_indication: code.s1("std_logic").subtype_indication(),
|
||||
expression: None
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
span: code.token_span()
|
||||
})
|
||||
|
|
@ -515,6 +521,7 @@ mod tests {
|
|||
code.with_stream(parse_parameter),
|
||||
InterfaceDeclaration::File(InterfaceFileDeclaration {
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("text").subtype_indication(),
|
||||
span: code.token_span()
|
||||
})
|
||||
|
|
@ -570,6 +577,7 @@ mod tests {
|
|||
items: vec![InterfaceDeclaration::File(InterfaceFileDeclaration {
|
||||
idents: vec![code.s1("valid").decl_ident()],
|
||||
subtype_indication: code.s("text", 2).subtype_indication(),
|
||||
colon_token: code.s(":", 2).token(),
|
||||
span: code.s1("file valid : text").token_span()
|
||||
})],
|
||||
span: code.token_span()
|
||||
|
|
@ -604,6 +612,7 @@ mod tests {
|
|||
expression: None
|
||||
}),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
@ -668,7 +677,7 @@ mod tests {
|
|||
assert_matches!(
|
||||
result.mode,
|
||||
ModeIndication::Simple(SimpleModeIndication {
|
||||
mode: Some(Mode::In),
|
||||
mode: Some(WithToken { item: Mode::In, .. }),
|
||||
class: ObjectClass::Constant,
|
||||
..
|
||||
})
|
||||
|
|
@ -679,7 +688,10 @@ mod tests {
|
|||
assert_matches!(
|
||||
result.mode,
|
||||
ModeIndication::Simple(SimpleModeIndication {
|
||||
mode: Some(Mode::Out),
|
||||
mode: Some(WithToken {
|
||||
item: Mode::Out,
|
||||
..
|
||||
}),
|
||||
class: ObjectClass::Variable,
|
||||
..
|
||||
})
|
||||
|
|
@ -690,7 +702,10 @@ mod tests {
|
|||
assert_matches!(
|
||||
result.mode,
|
||||
ModeIndication::Simple(SimpleModeIndication {
|
||||
mode: Some(Mode::InOut),
|
||||
mode: Some(WithToken {
|
||||
item: Mode::InOut,
|
||||
..
|
||||
}),
|
||||
class: ObjectClass::Variable,
|
||||
..
|
||||
})
|
||||
|
|
@ -712,6 +727,7 @@ mod tests {
|
|||
expression: None
|
||||
}),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
@ -732,6 +748,7 @@ mod tests {
|
|||
expression: None
|
||||
}),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
@ -743,7 +760,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_partial_stream(parse_generic),
|
||||
Err(Diagnostic::syntax_error(
|
||||
&code.s1("out").pos(),
|
||||
code.s1("out").pos(),
|
||||
"Interface constant declaration may only have mode=in"
|
||||
))
|
||||
);
|
||||
|
|
@ -1033,8 +1050,11 @@ package foo is new lib.pkg
|
|||
InterfaceDeclaration::Package(InterfacePackageDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
package_name: code.s1("lib.pkg").name(),
|
||||
generic_map: InterfacePackageGenericMapAspect::Map(
|
||||
code.s1("(foo => bar)").association_list()
|
||||
generic_map: WithTokenSpan::new(
|
||||
InterfacePackageGenericMapAspect::Map(
|
||||
code.s1("(foo => bar)").association_list()
|
||||
),
|
||||
code.between("generic", ")").token_span()
|
||||
),
|
||||
span: code.token_span()
|
||||
})
|
||||
|
|
@ -1053,7 +1073,10 @@ package foo is new lib.pkg
|
|||
InterfaceDeclaration::Package(InterfacePackageDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
package_name: code.s1("lib.pkg").name(),
|
||||
generic_map: InterfacePackageGenericMapAspect::Box,
|
||||
generic_map: WithTokenSpan::new(
|
||||
InterfacePackageGenericMapAspect::Box,
|
||||
code.between("generic", ")").token_span()
|
||||
),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
@ -1071,7 +1094,10 @@ package foo is new lib.pkg
|
|||
InterfaceDeclaration::Package(InterfacePackageDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
package_name: code.s1("lib.pkg").name(),
|
||||
generic_map: InterfacePackageGenericMapAspect::Default,
|
||||
generic_map: WithTokenSpan::new(
|
||||
InterfacePackageGenericMapAspect::Default,
|
||||
code.between("generic", ")").token_span()
|
||||
),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
@ -1131,6 +1157,7 @@ function foo() return bit;
|
|||
expression: None
|
||||
}),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
@ -1146,8 +1173,10 @@ function foo() return bit;
|
|||
mode: ModeIndication::View(ModeViewIndication {
|
||||
name: code.s1("bar").name(),
|
||||
subtype_indication: None,
|
||||
kind: ModeViewIndicationKind::Record
|
||||
kind: ModeViewIndicationKind::Record,
|
||||
span: code.s1("view bar").token_span(),
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
span: code.token_span()
|
||||
})
|
||||
|
|
@ -1161,9 +1190,11 @@ function foo() return bit;
|
|||
mode: ModeIndication::View(ModeViewIndication {
|
||||
name: code.s1("bar").name(),
|
||||
subtype_indication: None,
|
||||
kind: ModeViewIndicationKind::Array
|
||||
kind: ModeViewIndicationKind::Array,
|
||||
span: code.s1("view (bar)").token_span(),
|
||||
}),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
@ -1174,9 +1205,14 @@ function foo() return bit;
|
|||
list_type: InterfaceType::Port,
|
||||
mode: ModeIndication::View(ModeViewIndication {
|
||||
name: code.s1("bar").name(),
|
||||
subtype_indication: Some(code.s1("baz").subtype_indication()),
|
||||
kind: ModeViewIndicationKind::Record
|
||||
subtype_indication: Some((
|
||||
code.s1("of").token(),
|
||||
code.s1("baz").subtype_indication()
|
||||
)),
|
||||
kind: ModeViewIndicationKind::Record,
|
||||
span: code.s1("view bar of baz").token_span(),
|
||||
}),
|
||||
colon_token: code.s1(":").token(),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
span: code.token_span()
|
||||
})
|
||||
|
|
@ -1188,10 +1224,15 @@ function foo() return bit;
|
|||
list_type: InterfaceType::Port,
|
||||
mode: ModeIndication::View(ModeViewIndication {
|
||||
name: code.s1("bar").name(),
|
||||
subtype_indication: Some(code.s1("baz").subtype_indication()),
|
||||
kind: ModeViewIndicationKind::Array
|
||||
subtype_indication: Some((
|
||||
code.s1("of").token(),
|
||||
code.s1("baz").subtype_indication()
|
||||
)),
|
||||
kind: ModeViewIndicationKind::Array,
|
||||
span: code.s1("view (bar) of baz").token_span(),
|
||||
}),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.token_span()
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -249,23 +249,25 @@ fn parse_function_call(
|
|||
ctx: &mut ParsingContext<'_>,
|
||||
prefix: WithTokenSpan<Name>,
|
||||
first: AssociationElement,
|
||||
first_token: TokenId,
|
||||
) -> ParseResult<WithTokenSpan<Name>> {
|
||||
let mut association_elements = Vec::new();
|
||||
association_elements.push(first);
|
||||
let mut list = SeparatedList::default();
|
||||
list.items.push(first);
|
||||
list.tokens.push(first_token);
|
||||
|
||||
loop {
|
||||
association_elements.push(parse_association_element(ctx)?);
|
||||
list.items.push(parse_association_element(ctx)?);
|
||||
expect_token!(
|
||||
ctx.stream,
|
||||
token,
|
||||
token_id,
|
||||
Comma => {},
|
||||
Comma => list.tokens.push(token_id),
|
||||
RightPar => {
|
||||
let span = TokenSpan::new(prefix.span.start_token, token_id);
|
||||
return Ok(WithTokenSpan {
|
||||
item: Name::CallOrIndexed(Box::new(CallOrIndexed {
|
||||
name: prefix,
|
||||
parameters: association_elements})),
|
||||
parameters: list})),
|
||||
span,
|
||||
});
|
||||
}
|
||||
|
|
@ -372,12 +374,13 @@ fn parse_inner_external_name(ctx: &mut ParsingContext<'_>) -> ParseResult<Extern
|
|||
}
|
||||
);
|
||||
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let subtype = parse_subtype_indication(ctx)?;
|
||||
|
||||
Ok(ExternalName {
|
||||
class,
|
||||
path,
|
||||
colon_token,
|
||||
subtype,
|
||||
})
|
||||
}
|
||||
|
|
@ -471,7 +474,7 @@ fn _parse_name(ctx: &mut ParsingContext<'_>) -> ParseResult<WithTokenSpan<Name>>
|
|||
sep_token,
|
||||
sep_token_id,
|
||||
Comma => {
|
||||
name = parse_function_call(ctx, name, assoc)?;
|
||||
name = parse_function_call(ctx, name, assoc, sep_token_id)?;
|
||||
},
|
||||
To | Downto => {
|
||||
let right_expr = parse_expression(ctx)?;
|
||||
|
|
@ -502,7 +505,7 @@ fn _parse_name(ctx: &mut ParsingContext<'_>) -> ParseResult<WithTokenSpan<Name>>
|
|||
Ok(range) => Name::Slice(Box::new(name), Box::new(DiscreteRange::Range(range))),
|
||||
Err(assoc) => Name::CallOrIndexed(Box::new(CallOrIndexed {
|
||||
name,
|
||||
parameters: vec![assoc],
|
||||
parameters: SeparatedList::single(assoc),
|
||||
})),
|
||||
};
|
||||
|
||||
|
|
@ -980,10 +983,10 @@ mod tests {
|
|||
let foo_0 = WithTokenSpan {
|
||||
item: Name::CallOrIndexed(Box::new(CallOrIndexed {
|
||||
name: foo,
|
||||
parameters: vec![AssociationElement {
|
||||
parameters: SeparatedList::single(AssociationElement {
|
||||
formal: None,
|
||||
actual: code.s1("0").expr().map_into(ActualPart::Expression),
|
||||
}],
|
||||
}),
|
||||
})),
|
||||
span: code.s1("foo(0)").token_span(),
|
||||
};
|
||||
|
|
@ -1003,16 +1006,19 @@ mod tests {
|
|||
let prefix_index = WithTokenSpan {
|
||||
item: Name::CallOrIndexed(Box::new(CallOrIndexed {
|
||||
name: prefix,
|
||||
parameters: vec![
|
||||
AssociationElement {
|
||||
formal: None,
|
||||
actual: code.s1("0").expr().map_into(ActualPart::Expression),
|
||||
},
|
||||
AssociationElement {
|
||||
formal: None,
|
||||
actual: code.s1("1").expr().map_into(ActualPart::Expression),
|
||||
},
|
||||
],
|
||||
parameters: SeparatedList {
|
||||
items: vec![
|
||||
AssociationElement {
|
||||
formal: None,
|
||||
actual: code.s1("0").expr().map_into(ActualPart::Expression),
|
||||
},
|
||||
AssociationElement {
|
||||
formal: None,
|
||||
actual: code.s1("1").expr().map_into(ActualPart::Expression),
|
||||
},
|
||||
],
|
||||
tokens: vec![code.s1(",").token()],
|
||||
},
|
||||
})),
|
||||
span: code.s1("prefix(0, 1)").token_span(),
|
||||
};
|
||||
|
|
@ -1020,10 +1026,10 @@ mod tests {
|
|||
let prefix_index_3 = WithTokenSpan {
|
||||
item: Name::CallOrIndexed(Box::new(CallOrIndexed {
|
||||
name: prefix_index,
|
||||
parameters: vec![AssociationElement {
|
||||
parameters: SeparatedList::single(AssociationElement {
|
||||
formal: None,
|
||||
actual: code.s1("3").expr().map_into(ActualPart::Expression),
|
||||
}],
|
||||
}),
|
||||
})),
|
||||
span: code.s1("prefix(0, 1)(3)").token_span(),
|
||||
};
|
||||
|
|
@ -1063,7 +1069,7 @@ mod tests {
|
|||
let foo_call = WithTokenSpan {
|
||||
item: Name::CallOrIndexed(Box::new(CallOrIndexed {
|
||||
name: foo,
|
||||
parameters: vec![assoc_elem],
|
||||
parameters: SeparatedList::single(assoc_elem),
|
||||
})),
|
||||
span: code.s1("foo(arg => 0)").token_span(),
|
||||
};
|
||||
|
|
@ -1103,6 +1109,7 @@ mod tests {
|
|||
ExternalPath::Relative(code.s1("dut.foo").name(), 0),
|
||||
code.s1("dut.foo").token_span(),
|
||||
),
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype: code.s1("std_logic").subtype_indication(),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -1120,6 +1127,7 @@ mod tests {
|
|||
ExternalPath::Relative(code.s1("dut.gen(0)").name(), 1),
|
||||
code.s1("^.dut.gen(0)").token_span(),
|
||||
),
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype: code.s1("std_logic").subtype_indication(),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -1137,6 +1145,7 @@ mod tests {
|
|||
ExternalPath::Relative(code.s1("dut.gen(0)").name(), 3),
|
||||
code.s1("^.^.^.dut.gen(0)").token_span(),
|
||||
),
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype: code.s1("std_logic").subtype_indication(),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -1154,6 +1163,7 @@ mod tests {
|
|||
ExternalPath::Absolute(code.s1("dut.gen(0)").name()),
|
||||
code.s1(".dut.gen(0)").token_span(),
|
||||
),
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype: code.s1("std_logic").subtype_indication(),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -1171,6 +1181,7 @@ mod tests {
|
|||
ExternalPath::Package(code.s1("lib.pkg").name()),
|
||||
code.s1("@lib.pkg").token_span(),
|
||||
),
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype: code.s1("std_logic").subtype_indication(),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -1194,6 +1205,7 @@ mod tests {
|
|||
ExternalPath::Relative(code.s1("dut.foo").name(), 0),
|
||||
code.s1("dut.foo").token_span(),
|
||||
),
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype: code.s1("std_logic").subtype_indication(),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ fn parse_object_declaration_kind(
|
|||
.into_iter()
|
||||
.map(WithDecl::new)
|
||||
.collect_vec();
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let subtype = parse_subtype_indication(ctx)?;
|
||||
let opt_expression = parse_optional_assignment(ctx)?;
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
|
|
@ -60,6 +60,7 @@ fn parse_object_declaration_kind(
|
|||
ObjectDeclaration {
|
||||
class,
|
||||
idents,
|
||||
colon_token,
|
||||
subtype_indication: subtype.clone(),
|
||||
expression: opt_expression.clone(),
|
||||
},
|
||||
|
|
@ -85,23 +86,26 @@ pub fn parse_object_declaration(
|
|||
|
||||
pub fn parse_file_declaration(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
) -> ParseResult<Vec<WithTokenSpan<FileDeclaration>>> {
|
||||
) -> ParseResult<WithTokenSpan<FileDeclaration>> {
|
||||
let start_token = ctx.stream.expect_kind(File)?;
|
||||
let idents = parse_identifier_list(ctx)?;
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let idents = parse_identifier_list(ctx)?
|
||||
.into_iter()
|
||||
.map(WithDecl::new)
|
||||
.collect_vec();
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let subtype = parse_subtype_indication(ctx)?;
|
||||
|
||||
let open_info = {
|
||||
if ctx.stream.skip_if_kind(Open) {
|
||||
Some(parse_expression(ctx)?)
|
||||
if let Some(token) = ctx.stream.pop_if_kind(Open) {
|
||||
Some((token, parse_expression(ctx)?))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let file_name = {
|
||||
if ctx.stream.skip_if_kind(Is) {
|
||||
Some(parse_expression(ctx)?)
|
||||
if let Some(token) = ctx.stream.pop_if_kind(Is) {
|
||||
Some((token, parse_expression(ctx)?))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -113,26 +117,22 @@ pub fn parse_file_declaration(
|
|||
if open_info.is_some() && file_name.is_none() {
|
||||
if let Some(ident) = idents.first() {
|
||||
return Err(Diagnostic::syntax_error(
|
||||
ident.pos(ctx),
|
||||
ident.tree.pos(ctx),
|
||||
"file_declaration must have a file name specified if the file open expression is specified as well",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(idents
|
||||
.into_iter()
|
||||
.map(|ident| {
|
||||
WithTokenSpan::new(
|
||||
FileDeclaration {
|
||||
ident: ident.into(),
|
||||
subtype_indication: subtype.clone(),
|
||||
open_info: open_info.clone(),
|
||||
file_name: file_name.clone(),
|
||||
},
|
||||
TokenSpan::new(start_token, end_token),
|
||||
)
|
||||
})
|
||||
.collect())
|
||||
Ok(WithTokenSpan::new(
|
||||
FileDeclaration {
|
||||
idents,
|
||||
colon_token,
|
||||
subtype_indication: subtype.clone(),
|
||||
open_info: open_info.clone(),
|
||||
file_name: file_name.clone(),
|
||||
},
|
||||
TokenSpan::new(start_token, end_token),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -152,6 +152,7 @@ mod tests {
|
|||
ObjectDeclaration {
|
||||
class: ObjectClass::Constant,
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("natural").subtype_indication(),
|
||||
expression: None
|
||||
},
|
||||
|
|
@ -169,6 +170,7 @@ mod tests {
|
|||
ObjectDeclaration {
|
||||
class: ObjectClass::Signal,
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("natural").subtype_indication(),
|
||||
expression: None
|
||||
},
|
||||
|
|
@ -186,6 +188,7 @@ mod tests {
|
|||
ObjectDeclaration {
|
||||
class: ObjectClass::Variable,
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("natural").subtype_indication(),
|
||||
expression: None
|
||||
},
|
||||
|
|
@ -203,6 +206,7 @@ mod tests {
|
|||
ObjectDeclaration {
|
||||
class: ObjectClass::SharedVariable,
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("natural").subtype_indication(),
|
||||
expression: None
|
||||
},
|
||||
|
|
@ -216,15 +220,16 @@ mod tests {
|
|||
let code = Code::new("file foo : text;");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_file_declaration),
|
||||
vec![WithTokenSpan::new(
|
||||
WithTokenSpan::new(
|
||||
FileDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("text").subtype_indication(),
|
||||
open_info: None,
|
||||
file_name: None
|
||||
},
|
||||
code.token_span()
|
||||
)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -233,15 +238,16 @@ mod tests {
|
|||
let code = Code::new("file foo : text is \"file_name\";");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_file_declaration),
|
||||
vec![WithTokenSpan::new(
|
||||
WithTokenSpan::new(
|
||||
FileDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("text").subtype_indication(),
|
||||
open_info: None,
|
||||
file_name: Some(code.s1("\"file_name\"").expr())
|
||||
file_name: Some((code.s1("is").token(), code.s1("\"file_name\"").expr()))
|
||||
},
|
||||
code.token_span()
|
||||
)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -250,15 +256,16 @@ mod tests {
|
|||
let code = Code::new("file foo : text open write_mode is \"file_name\";");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_file_declaration),
|
||||
vec![WithTokenSpan::new(
|
||||
WithTokenSpan::new(
|
||||
FileDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("text").subtype_indication(),
|
||||
open_info: Some(code.s1("write_mode").expr()),
|
||||
file_name: Some(code.s1("\"file_name\"").expr())
|
||||
open_info: Some((code.s1("open").token(), code.s1("write_mode").expr())),
|
||||
file_name: Some((code.s1("is").token(), code.s1("\"file_name\"").expr()))
|
||||
},
|
||||
code.token_span()
|
||||
)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -283,6 +290,7 @@ mod tests {
|
|||
ObjectDeclaration {
|
||||
class: ObjectClass::Constant,
|
||||
idents: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("natural").subtype_indication(),
|
||||
expression: Some(code.s1("0").expr())
|
||||
},
|
||||
|
|
@ -301,6 +309,7 @@ mod tests {
|
|||
ObjectDeclaration {
|
||||
class: ObjectClass::Constant,
|
||||
idents: vec![code.s1("foo").decl_ident(), code.s1("bar").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("natural").subtype_indication(),
|
||||
expression: Some(code.s1("0").expr()),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,10 +26,14 @@ pub(crate) struct ParsingContext<'a> {
|
|||
}
|
||||
|
||||
impl TokenAccess for ParsingContext<'_> {
|
||||
fn get_token(&self, id: TokenId) -> &Token {
|
||||
fn get_token(&self, id: TokenId) -> Option<&Token> {
|
||||
self.stream.get_token(id)
|
||||
}
|
||||
|
||||
fn index(&self, id: TokenId) -> &Token {
|
||||
self.stream.index(id)
|
||||
}
|
||||
|
||||
fn get_token_slice(&self, start_id: TokenId, end_id: TokenId) -> &[Token] {
|
||||
self.stream.get_token_slice(start_id, end_id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,10 @@ pub fn parse_discrete_range(ctx: &mut ParsingContext<'_>) -> ParseResult<Discret
|
|||
|
||||
pub fn parse_array_index_constraint(ctx: &mut ParsingContext<'_>) -> ParseResult<ArrayIndex> {
|
||||
match parse_name_or_range(ctx) {
|
||||
Ok(NameOrRange::Range(range)) => Ok(ArrayIndex::Discrete(DiscreteRange::Range(range.item))),
|
||||
Ok(NameOrRange::Range(range)) => Ok(ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Range(range.item),
|
||||
range.span,
|
||||
))),
|
||||
Ok(NameOrRange::Name(name)) => {
|
||||
let type_mark = name_to_type_mark(ctx, name)?;
|
||||
|
||||
|
|
@ -106,14 +109,18 @@ pub fn parse_array_index_constraint(ctx: &mut ParsingContext<'_>) -> ParseResult
|
|||
if ctx.stream.skip_if_kind(BOX) {
|
||||
Ok(ArrayIndex::IndexSubtypeDefintion(type_mark))
|
||||
} else {
|
||||
Ok(ArrayIndex::Discrete(DiscreteRange::Discrete(
|
||||
type_mark,
|
||||
Some(parse_range(ctx)?.item),
|
||||
let range = parse_range(ctx)?;
|
||||
let span = type_mark.span.combine(range.span);
|
||||
Ok(ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Discrete(type_mark, Some(range.item)),
|
||||
span,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Ok(ArrayIndex::Discrete(DiscreteRange::Discrete(
|
||||
type_mark, None,
|
||||
let type_mark_span = type_mark.span;
|
||||
Ok(ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Discrete(type_mark, None),
|
||||
type_mark_span,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
@ -241,7 +248,10 @@ mod tests {
|
|||
let code = Code::new("0 to 1");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_array_index_constraint),
|
||||
ArrayIndex::Discrete(code.s1("0 to 1").discrete_range())
|
||||
ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
code.s1("0 to 1").discrete_range(),
|
||||
code.token_span()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +260,10 @@ mod tests {
|
|||
let code = Code::new("foo.bar range 0 to 1");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_array_index_constraint),
|
||||
ArrayIndex::Discrete(code.s1("foo.bar range 0 to 1").discrete_range())
|
||||
ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
code.s1("foo.bar range 0 to 1").discrete_range(),
|
||||
code.token_span()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +272,10 @@ mod tests {
|
|||
let code = Code::new("foo.bar");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_array_index_constraint),
|
||||
ArrayIndex::Discrete(code.s1("foo.bar").discrete_range())
|
||||
ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
code.s1("foo.bar").discrete_range(),
|
||||
code.token_span()
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ signal y: bit;
|
|||
Declaration::Object(ObjectDeclaration {
|
||||
class: ObjectClass::Signal,
|
||||
idents: vec![code.s1("x").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype_indication: code.s1("std_logic").subtype_indication(),
|
||||
expression: Some(code.s1("a.").s1("a").expr())
|
||||
}),
|
||||
|
|
@ -85,6 +86,7 @@ signal y: bit;
|
|||
Declaration::Object(ObjectDeclaration {
|
||||
class: ObjectClass::Signal,
|
||||
idents: vec![code.s1("y").decl_ident()],
|
||||
colon_token: code.s(":", 3).token(),
|
||||
subtype_indication: code.s1("bit").subtype_indication(),
|
||||
expression: None
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
//
|
||||
// Copyright (c) 2023, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::{IdentList, NameList, SeparatedList, WithRef};
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{Name, SeparatedList};
|
||||
use crate::data::DiagnosticResult;
|
||||
use crate::syntax::common::ParseResult;
|
||||
use crate::syntax::names::parse_name;
|
||||
|
|
@ -81,23 +82,15 @@ where
|
|||
Ok(SeparatedList { items, tokens })
|
||||
}
|
||||
|
||||
pub fn parse_name_list(ctx: &mut ParsingContext<'_>) -> DiagnosticResult<NameList> {
|
||||
parse_list_with_separator(ctx, Comma, parse_name)
|
||||
}
|
||||
|
||||
pub fn parse_ident_list(ctx: &mut ParsingContext<'_>) -> DiagnosticResult<IdentList> {
|
||||
parse_list_with_separator(ctx, Comma, |ctx| {
|
||||
ctx.stream.expect_ident().map(WithRef::new)
|
||||
})
|
||||
pub fn parse_name_list(ctx: &mut ParsingContext<'_>) -> DiagnosticResult<Vec<WithTokenSpan<Name>>> {
|
||||
Ok(parse_list_with_separator(ctx, Comma, parse_name)?.items)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::ast::{IdentList, NameList, SeparatedList};
|
||||
use crate::ast::SeparatedList;
|
||||
use crate::syntax::names::parse_association_element;
|
||||
use crate::syntax::separated_list::{
|
||||
parse_ident_list, parse_list_with_separator_or_recover, parse_name_list,
|
||||
};
|
||||
use crate::syntax::separated_list::{parse_list_with_separator_or_recover, parse_name_list};
|
||||
use crate::syntax::test::Code;
|
||||
use crate::syntax::Kind;
|
||||
use crate::syntax::Kind::RightPar;
|
||||
|
|
@ -106,7 +99,7 @@ mod test {
|
|||
#[test]
|
||||
pub fn test_error_on_empty_list() {
|
||||
let code = Code::new("");
|
||||
let (res, diagnostics) = code.with_partial_stream_diagnostics(parse_ident_list);
|
||||
let (res, diagnostics) = code.with_partial_stream_diagnostics(parse_name_list);
|
||||
assert_eq!(
|
||||
res,
|
||||
Err(Diagnostic::syntax_error(code.eof_pos(), "Unexpected EOF"))
|
||||
|
|
@ -118,24 +111,8 @@ mod test {
|
|||
pub fn parse_single_element_list() {
|
||||
let code = Code::new("abc");
|
||||
assert_eq!(
|
||||
code.parse_ok_no_diagnostics(parse_ident_list),
|
||||
IdentList::single(code.s1("abc").ident().into_ref())
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_list_with_multiple_elements() {
|
||||
let code = Code::new("abc, def, ghi");
|
||||
assert_eq!(
|
||||
code.parse_ok_no_diagnostics(parse_ident_list),
|
||||
IdentList {
|
||||
items: vec![
|
||||
code.s1("abc").ident().into_ref(),
|
||||
code.s1("def").ident().into_ref(),
|
||||
code.s1("ghi").ident().into_ref()
|
||||
],
|
||||
tokens: vec![code.s(",", 1).token(), code.s(",", 2).token()]
|
||||
}
|
||||
code.parse_ok_no_diagnostics(parse_name_list),
|
||||
vec![code.s1("abc").name()]
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -144,27 +121,21 @@ mod test {
|
|||
let code = Code::new("work.foo, lib.bar.all");
|
||||
assert_eq!(
|
||||
code.parse_ok_no_diagnostics(parse_name_list),
|
||||
NameList {
|
||||
items: vec![code.s1("work.foo").name(), code.s1("lib.bar.all").name()],
|
||||
tokens: vec![code.s1(",").token()],
|
||||
}
|
||||
vec![code.s1("work.foo").name(), code.s1("lib.bar.all").name()]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_extraneous_single_separators() {
|
||||
let code = Code::new("a,,b,c");
|
||||
let (res, diag) = code.with_stream_diagnostics(parse_ident_list);
|
||||
let (res, diag) = code.with_stream_diagnostics(parse_name_list);
|
||||
assert_eq!(
|
||||
res,
|
||||
IdentList {
|
||||
items: vec![
|
||||
code.s1("a").ident().into_ref(),
|
||||
code.s1("b").ident().into_ref(),
|
||||
code.s1("c").ident().into_ref()
|
||||
],
|
||||
tokens: vec![code.s(",", 1).token(), code.s(",", 3).token()]
|
||||
}
|
||||
vec![
|
||||
code.s1("a").name(),
|
||||
code.s1("b").name(),
|
||||
code.s1("c").name()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
diag,
|
||||
|
|
@ -178,17 +149,14 @@ mod test {
|
|||
#[test]
|
||||
fn parse_extraneous_multiple_separators() {
|
||||
let code = Code::new("a,,,,b,c");
|
||||
let (res, diag) = code.with_stream_diagnostics(parse_ident_list);
|
||||
let (res, diag) = code.with_stream_diagnostics(parse_name_list);
|
||||
assert_eq!(
|
||||
res,
|
||||
IdentList {
|
||||
items: vec![
|
||||
code.s1("a").ident().into_ref(),
|
||||
code.s1("b").ident().into_ref(),
|
||||
code.s1("c").ident().into_ref()
|
||||
],
|
||||
tokens: vec![code.s(",", 1).token(), code.s(",", 5).token()]
|
||||
}
|
||||
vec![
|
||||
code.s1("a").name(),
|
||||
code.s1("b").name(),
|
||||
code.s1("c").name()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
diag,
|
||||
|
|
@ -235,12 +203,15 @@ mod test {
|
|||
fn parse_list_with_erroneous_elements() {
|
||||
let code = Code::new("1,c,d");
|
||||
let diag = code
|
||||
.parse(parse_ident_list)
|
||||
.parse(parse_name_list)
|
||||
.0
|
||||
.expect_err("Should not parse OK");
|
||||
assert_eq!(
|
||||
diag,
|
||||
Diagnostic::syntax_error(code.s1("1"), "Expected '{identifier}'")
|
||||
Diagnostic::syntax_error(
|
||||
code.s1("1"),
|
||||
"Expected '{identifier}', '{character}', '{string}' or 'all'"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use crate::data::*;
|
|||
use crate::syntax::common::check_label_identifier_mismatch;
|
||||
use crate::syntax::kinds_error;
|
||||
use crate::syntax::recover::{expect_semicolon, expect_semicolon_or_last};
|
||||
use crate::syntax::separated_list::parse_name_list;
|
||||
use crate::HasTokenSpan;
|
||||
use vhdl_lang::syntax::parser::ParsingContext;
|
||||
use vhdl_lang::TokenSpan;
|
||||
|
|
@ -25,15 +26,11 @@ use vhdl_lang::TokenSpan;
|
|||
/// LRM 10.2 Wait statement
|
||||
fn parse_wait_statement(ctx: &mut ParsingContext<'_>) -> ParseResult<WaitStatement> {
|
||||
ctx.stream.expect_kind(Wait)?;
|
||||
let mut sensitivity_clause = vec![];
|
||||
if ctx.stream.skip_if_kind(On) {
|
||||
loop {
|
||||
sensitivity_clause.push(parse_name(ctx)?);
|
||||
if !ctx.stream.skip_if_kind(Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let sensitivity_clause = if ctx.stream.skip_if_kind(On) {
|
||||
Some(parse_name_list(ctx)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let condition_clause = parse_optional(ctx, Until, parse_expression)?;
|
||||
let timeout_clause = parse_optional(ctx, For, parse_expression)?;
|
||||
|
|
@ -115,6 +112,7 @@ fn parse_if_statement(
|
|||
expect_token!(
|
||||
ctx.stream,
|
||||
end_token,
|
||||
token_id,
|
||||
Elsif => {
|
||||
conditionals.push(conditional);
|
||||
continue;
|
||||
|
|
@ -129,7 +127,7 @@ fn parse_if_statement(
|
|||
end_token,
|
||||
End => {
|
||||
ctx.stream.expect_kind(If)?;
|
||||
else_branch = Some(statements);
|
||||
else_branch = Some((statements, token_id));
|
||||
break;
|
||||
}
|
||||
);
|
||||
|
|
@ -167,17 +165,21 @@ fn parse_case_statement(
|
|||
let mut alternatives = Vec::new();
|
||||
|
||||
loop {
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
let choices = parse_choices(ctx)?;
|
||||
ctx.stream.expect_kind(RightArrow)?;
|
||||
let statements = parse_labeled_sequential_statements(ctx)?;
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
let alternative = Alternative {
|
||||
choices,
|
||||
item: statements,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
};
|
||||
|
||||
expect_token!(
|
||||
ctx.stream,
|
||||
end_token,
|
||||
end_token_id,
|
||||
When => {
|
||||
alternatives.push(alternative);
|
||||
continue;
|
||||
|
|
@ -194,6 +196,7 @@ fn parse_case_statement(
|
|||
is_matching,
|
||||
expression,
|
||||
alternatives,
|
||||
end_token: end_token_id,
|
||||
end_label_pos,
|
||||
});
|
||||
}
|
||||
|
|
@ -206,21 +209,21 @@ fn parse_loop_statement(
|
|||
ctx: &mut ParsingContext<'_>,
|
||||
label: Option<&Ident>,
|
||||
) -> ParseResult<LoopStatement> {
|
||||
let iteration_scheme = {
|
||||
let (iteration_scheme, loop_token) = {
|
||||
expect_token!(
|
||||
ctx.stream, token,
|
||||
Loop => None,
|
||||
ctx.stream, token, token_id,
|
||||
Loop => (None, token_id),
|
||||
While => {
|
||||
let expression = parse_expression(ctx)?;
|
||||
ctx.stream.expect_kind(Loop)?;
|
||||
Some(IterationScheme::While(expression))
|
||||
let loop_token = ctx.stream.expect_kind(Loop)?;
|
||||
(Some(IterationScheme::While(expression)), loop_token)
|
||||
},
|
||||
For => {
|
||||
let ident = ctx.stream.expect_ident()?;
|
||||
ctx.stream.expect_kind(In)?;
|
||||
let discrete_range = parse_discrete_range(ctx)?;
|
||||
ctx.stream.expect_kind(Loop)?;
|
||||
Some(IterationScheme::For(ident.into(), discrete_range))
|
||||
let loop_token = ctx.stream.expect_kind(Loop)?;
|
||||
(Some(IterationScheme::For(ident.into(), discrete_range)), loop_token)
|
||||
}
|
||||
)
|
||||
};
|
||||
|
|
@ -230,13 +233,16 @@ fn parse_loop_statement(
|
|||
expect_token!(
|
||||
ctx.stream,
|
||||
end_token,
|
||||
end_token_id,
|
||||
End => {
|
||||
ctx.stream.expect_kind(Loop)?;
|
||||
let end_label_pos = check_label_identifier_mismatch(ctx, label, ctx.stream.pop_optional_ident());
|
||||
expect_semicolon(ctx);
|
||||
Ok(LoopStatement {
|
||||
iteration_scheme,
|
||||
loop_token,
|
||||
statements,
|
||||
end_token: end_token_id,
|
||||
end_label_pos,
|
||||
})
|
||||
}
|
||||
|
|
@ -352,6 +358,7 @@ where
|
|||
expect_token!(
|
||||
ctx.stream,
|
||||
token,
|
||||
token_id,
|
||||
SemiColon => {
|
||||
break;
|
||||
},
|
||||
|
|
@ -361,7 +368,7 @@ where
|
|||
ctx.stream,
|
||||
token,
|
||||
SemiColon => {
|
||||
else_item = Some(item);
|
||||
else_item = Some((item, token_id));
|
||||
break;
|
||||
},
|
||||
When => {
|
||||
|
|
@ -394,10 +401,16 @@ where
|
|||
let mut alternatives = Vec::with_capacity(2);
|
||||
|
||||
loop {
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
let item = parse_item(ctx)?;
|
||||
ctx.stream.expect_kind(When)?;
|
||||
let choices = parse_choices(ctx)?;
|
||||
alternatives.push(Alternative { choices, item });
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
alternatives.push(Alternative {
|
||||
choices,
|
||||
item,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
});
|
||||
|
||||
expect_token!(
|
||||
ctx.stream,
|
||||
|
|
@ -485,7 +498,7 @@ fn parse_assignment_or_procedure_call(
|
|||
SequentialStatement::ProcedureCall(
|
||||
WithTokenSpan::from(CallOrIndexed {
|
||||
name: WithTokenSpan::from(name, target.span),
|
||||
parameters: vec![]
|
||||
parameters: SeparatedList::default(),
|
||||
}, target.span))
|
||||
}
|
||||
Target::Aggregate(..) => {
|
||||
|
|
@ -644,7 +657,7 @@ mod tests {
|
|||
None,
|
||||
WithTokenSpan::from(
|
||||
SequentialStatement::Wait(WaitStatement {
|
||||
sensitivity_clause: vec![],
|
||||
sensitivity_clause: None,
|
||||
condition_clause: None,
|
||||
timeout_clause: None,
|
||||
}),
|
||||
|
|
@ -663,7 +676,7 @@ mod tests {
|
|||
Some(code.s1("foo").ident()),
|
||||
WithTokenSpan::new(
|
||||
SequentialStatement::Wait(WaitStatement {
|
||||
sensitivity_clause: vec![],
|
||||
sensitivity_clause: None,
|
||||
condition_clause: None,
|
||||
timeout_clause: None,
|
||||
}),
|
||||
|
|
@ -682,7 +695,10 @@ mod tests {
|
|||
None,
|
||||
WithTokenSpan::new(
|
||||
SequentialStatement::Wait(WaitStatement {
|
||||
sensitivity_clause: vec![code.s1("foo").name(), code.s1("bar").name()],
|
||||
sensitivity_clause: Some(vec![
|
||||
code.s1("foo").name(),
|
||||
code.s1("bar").name()
|
||||
]),
|
||||
condition_clause: None,
|
||||
timeout_clause: None,
|
||||
}),
|
||||
|
|
@ -701,7 +717,7 @@ mod tests {
|
|||
None,
|
||||
WithTokenSpan::new(
|
||||
SequentialStatement::Wait(WaitStatement {
|
||||
sensitivity_clause: vec![],
|
||||
sensitivity_clause: None,
|
||||
condition_clause: Some(code.s1("a = b").expr()),
|
||||
timeout_clause: None,
|
||||
}),
|
||||
|
|
@ -720,7 +736,7 @@ mod tests {
|
|||
None,
|
||||
WithTokenSpan::new(
|
||||
SequentialStatement::Wait(WaitStatement {
|
||||
sensitivity_clause: vec![],
|
||||
sensitivity_clause: None,
|
||||
condition_clause: None,
|
||||
timeout_clause: Some(code.s1("2 ns").expr()),
|
||||
}),
|
||||
|
|
@ -739,7 +755,7 @@ mod tests {
|
|||
None,
|
||||
WithTokenSpan::new(
|
||||
SequentialStatement::Wait(WaitStatement {
|
||||
sensitivity_clause: vec![code.s1("foo").name()],
|
||||
sensitivity_clause: Some(vec![code.s1("foo").name()]),
|
||||
condition_clause: Some(code.s1("bar").expr()),
|
||||
timeout_clause: Some(code.s1("2 ns").expr()),
|
||||
}),
|
||||
|
|
@ -935,7 +951,10 @@ mod tests {
|
|||
WithTokenSpan::new(
|
||||
SequentialStatement::SignalAssignment(SignalAssignment {
|
||||
target: code.s1("foo(0)").name().map_into(Target::Name),
|
||||
delay_mechanism: Some(DelayMechanism::Transport),
|
||||
delay_mechanism: Some(WithTokenSpan::new(
|
||||
DelayMechanism::Transport,
|
||||
code.s1("transport").token_span()
|
||||
)),
|
||||
rhs: AssignmentRightHand::Simple(code.s1("bar(1,2)").waveform()),
|
||||
}),
|
||||
code.token_span()
|
||||
|
|
@ -1082,10 +1101,12 @@ with x(0) + 1 select
|
|||
Alternative {
|
||||
choices: code.s1("0|1").choices(),
|
||||
item: code.s1("bar(1,2)").expr(),
|
||||
span: code.s1("bar(1,2) when 0|1").token_span(),
|
||||
},
|
||||
Alternative {
|
||||
choices: code.s1("others").choices(),
|
||||
item: code.s1("def").expr(),
|
||||
span: code.s1("def when others").token_span(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -1149,7 +1170,7 @@ with x(0) + 1 select
|
|||
condition: code.s1("cond = true").expr(),
|
||||
item: code.s1("bar(1,2)").expr()
|
||||
}],
|
||||
else_item: Some(code.s1("expr2").expr())
|
||||
else_item: Some((code.s1("expr2").expr(), code.s1("else").token()))
|
||||
}),
|
||||
}),
|
||||
code.token_span()
|
||||
|
|
@ -1229,10 +1250,12 @@ with x(0) + 1 select
|
|||
Alternative {
|
||||
choices: code.s1("0|1").choices(),
|
||||
item: code.s1("bar(1,2) after 2 ns").waveform(),
|
||||
span: code.s1("bar(1,2) after 2 ns when 0|1").token_span(),
|
||||
},
|
||||
Alternative {
|
||||
choices: code.s1("others").choices(),
|
||||
item: code.s1("def").waveform(),
|
||||
span: code.s1("def when others").token_span(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -1244,7 +1267,10 @@ with x(0) + 1 select
|
|||
WithTokenSpan::new(
|
||||
SequentialStatement::SignalAssignment(SignalAssignment {
|
||||
target: code.s1("foo(0)").name().map_into(Target::Name),
|
||||
delay_mechanism: Some(DelayMechanism::Transport),
|
||||
delay_mechanism: Some(WithTokenSpan::new(
|
||||
DelayMechanism::Transport,
|
||||
code.s1("transport").token_span()
|
||||
)),
|
||||
rhs: AssignmentRightHand::Selected(selection),
|
||||
}),
|
||||
code.token_span()
|
||||
|
|
@ -1268,10 +1294,12 @@ with x(0) + 1 select
|
|||
Alternative {
|
||||
choices: code.s1("0|1").choices(),
|
||||
item: code.s1("bar(1,2)").expr(),
|
||||
span: code.s1("bar(1,2) when 0|1").token_span(),
|
||||
},
|
||||
Alternative {
|
||||
choices: code.s1("others").choices(),
|
||||
item: code.s1("def").expr(),
|
||||
span: code.s1("def when others").token_span(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -1410,7 +1438,10 @@ end if;",
|
|||
condition: code.s1("cond = true").expr(),
|
||||
item: vec![code.s1("foo(1,2);").sequential_statement()]
|
||||
}],
|
||||
else_item: Some(vec![code.s1("x := 1;").sequential_statement()])
|
||||
else_item: Some((
|
||||
vec![code.s1("x := 1;").sequential_statement()],
|
||||
code.s1("else").token()
|
||||
))
|
||||
},
|
||||
end_label_pos: None,
|
||||
}),
|
||||
|
|
@ -1441,7 +1472,10 @@ end if mylabel;",
|
|||
condition: code.s1("cond = true").expr(),
|
||||
item: vec![code.s1("foo(1,2);").sequential_statement()]
|
||||
}],
|
||||
else_item: Some(vec![code.s1("x := 1;").sequential_statement()])
|
||||
else_item: Some((
|
||||
vec![code.s1("x := 1;").sequential_statement()],
|
||||
code.s1("else").token()
|
||||
))
|
||||
},
|
||||
end_label_pos: Some(code.s("mylabel", 2).pos()),
|
||||
}),
|
||||
|
|
@ -1479,7 +1513,10 @@ end if;",
|
|||
item: vec![code.s1("y := 2;").sequential_statement()]
|
||||
}
|
||||
],
|
||||
else_item: Some(vec![code.s1("x := 1;").sequential_statement()])
|
||||
else_item: Some((
|
||||
vec![code.s1("x := 1;").sequential_statement()],
|
||||
code.s1("else").token()
|
||||
))
|
||||
},
|
||||
end_label_pos: None,
|
||||
}),
|
||||
|
|
@ -1517,7 +1554,10 @@ end if mylabel;",
|
|||
item: vec![code.s1("y := 2;").sequential_statement()]
|
||||
}
|
||||
],
|
||||
else_item: Some(vec![code.s1("x := 1;").sequential_statement()])
|
||||
else_item: Some((
|
||||
vec![code.s1("x := 1;").sequential_statement()],
|
||||
code.s1("else").token()
|
||||
))
|
||||
},
|
||||
end_label_pos: Some(code.s("mylabel", 2).pos()),
|
||||
}),
|
||||
|
|
@ -1553,16 +1593,27 @@ end case;",
|
|||
item: vec![
|
||||
code.s1("stmt1;").sequential_statement(),
|
||||
code.s1("stmt2;").sequential_statement()
|
||||
]
|
||||
],
|
||||
span: code
|
||||
.s1("1 | 2 =>
|
||||
stmt1;
|
||||
stmt2;")
|
||||
.token_span()
|
||||
},
|
||||
Alternative {
|
||||
choices: code.s1("others").choices(),
|
||||
item: vec![
|
||||
code.s1("stmt3;").sequential_statement(),
|
||||
code.s1("stmt4;").sequential_statement(),
|
||||
]
|
||||
],
|
||||
span: code
|
||||
.s1("others =>
|
||||
stmt3;
|
||||
stmt4;")
|
||||
.token_span()
|
||||
}
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
}),
|
||||
code.token_span()
|
||||
|
|
@ -1589,8 +1640,10 @@ end case?;",
|
|||
expression: code.s1("foo(1)").expr(),
|
||||
alternatives: vec![Alternative {
|
||||
choices: code.s1("others").choices(),
|
||||
item: vec![code.s1("null;").sequential_statement(),]
|
||||
item: vec![code.s1("null;").sequential_statement()],
|
||||
span: code.s1("others => null;").token_span()
|
||||
}],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
}),
|
||||
code.token_span()
|
||||
|
|
@ -1615,10 +1668,12 @@ end loop lbl;",
|
|||
WithTokenSpan::new(
|
||||
SequentialStatement::Loop(LoopStatement {
|
||||
iteration_scheme: None,
|
||||
loop_token: code.s1("loop").token(),
|
||||
statements: vec![
|
||||
code.s1("stmt1;").sequential_statement(),
|
||||
code.s1("stmt2;").sequential_statement()
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: Some(code.s("lbl", 2).pos()),
|
||||
}),
|
||||
code.pos_after("lbl: ").token_span()
|
||||
|
|
@ -1645,10 +1700,12 @@ end loop;",
|
|||
iteration_scheme: Some(IterationScheme::While(
|
||||
code.s1("foo = true").expr()
|
||||
)),
|
||||
loop_token: code.s1("loop").token(),
|
||||
statements: vec![
|
||||
code.s1("stmt1;").sequential_statement(),
|
||||
code.s1("stmt2;").sequential_statement()
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
}),
|
||||
code.token_span()
|
||||
|
|
@ -1675,10 +1732,12 @@ end loop;",
|
|||
code.s1("idx").decl_ident(),
|
||||
code.s1("0 to 3").discrete_range()
|
||||
)),
|
||||
loop_token: code.s1("loop").token(),
|
||||
statements: vec![
|
||||
code.s1("stmt1;").sequential_statement(),
|
||||
code.s1("stmt2;").sequential_statement()
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_label_pos: None,
|
||||
}),
|
||||
code.token_span()
|
||||
|
|
|
|||
|
|
@ -227,12 +227,13 @@ pub fn parse_subprogram_body(
|
|||
}
|
||||
};
|
||||
let declarations = parse_declarative_part(ctx)?;
|
||||
ctx.stream.expect_kind(Begin)?;
|
||||
let begin_token = ctx.stream.expect_kind(Begin)?;
|
||||
|
||||
let statements = parse_labeled_sequential_statements(ctx)?;
|
||||
expect_token!(
|
||||
ctx.stream,
|
||||
end_token,
|
||||
end_token_id,
|
||||
End => {
|
||||
ctx.stream.pop_if_kind(end_kind);
|
||||
|
||||
|
|
@ -241,14 +242,16 @@ pub fn parse_subprogram_body(
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
let semicolon = expect_semicolon_or_last(ctx);
|
||||
|
||||
Ok(SubprogramBody {
|
||||
span: TokenSpan::new(specification_start_token, end_token),
|
||||
span: TokenSpan::new(specification_start_token, semicolon),
|
||||
end_ident_pos: check_end_identifier_mismatch(ctx, specification.subpgm_designator(), end_ident),
|
||||
begin_token,
|
||||
specification,
|
||||
declarations,
|
||||
statements,
|
||||
end_token: end_token_id
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
@ -663,8 +666,10 @@ end function;
|
|||
let body = SubprogramBody {
|
||||
span: code.token_span(),
|
||||
specification,
|
||||
begin_token: code.s1("begin").token(),
|
||||
declarations,
|
||||
statements,
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -705,7 +710,9 @@ end function foo;
|
|||
span: code.token_span(),
|
||||
specification,
|
||||
declarations: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: Some(code.s("foo", 2).token()),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -730,7 +737,9 @@ end function \"+\";
|
|||
span: code.token_span(),
|
||||
specification,
|
||||
declarations: vec![],
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: Some(code.s("\"+\"", 2).token()),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -771,7 +780,7 @@ end function \"+\";
|
|||
header,
|
||||
SubprogramHeader {
|
||||
map_aspect: Some(MapAspect {
|
||||
start: code.s("generic", 2).token(),
|
||||
span: code.s1("generic map (x => 2, y => 0.4)").token_span(),
|
||||
list: SeparatedList {
|
||||
items: vec![
|
||||
code.s1("x => 2").association_element(),
|
||||
|
|
@ -779,7 +788,6 @@ end function \"+\";
|
|||
],
|
||||
tokens: vec![code.s1(",").token()]
|
||||
},
|
||||
closing_paren: code.s(")", 2).token()
|
||||
}),
|
||||
generic_list: InterfaceList {
|
||||
interface_type: InterfaceType::Generic,
|
||||
|
|
@ -872,8 +880,10 @@ end function;
|
|||
let body = SubprogramBody {
|
||||
span: code.token_span(),
|
||||
specification,
|
||||
begin_token: code.s1("begin").token(),
|
||||
declarations,
|
||||
statements,
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
@ -905,11 +915,13 @@ end procedure swap;
|
|||
span: code.token_span(),
|
||||
specification,
|
||||
declarations: code.s1("variable temp : T;").declarative_part(),
|
||||
begin_token: code.s1("begin").token(),
|
||||
statements: vec![
|
||||
code.s1("temp := a;").sequential_statement(),
|
||||
code.s1("a := b;").sequential_statement(),
|
||||
code.s1(" b := temp;").sequential_statement(),
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: Some(code.s("swap", 2).token()),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ fn parse_array_constraint(
|
|||
ctx: &mut ParsingContext<'_>,
|
||||
leftpar: TokenId,
|
||||
// Open is None
|
||||
initial: Option<DiscreteRange>,
|
||||
initial: Option<WithTokenSpan<DiscreteRange>>,
|
||||
) -> ParseResult<WithTokenSpan<SubtypeConstraint>> {
|
||||
let mut discrete_ranges: Vec<_> = initial.into_iter().collect();
|
||||
|
||||
|
|
@ -35,8 +35,14 @@ fn parse_array_constraint(
|
|||
RightPar => break sep_token_id,
|
||||
Comma => {}
|
||||
);
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
let drange = parse_discrete_range(ctx)?;
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
|
||||
discrete_ranges.push(parse_discrete_range(ctx)?);
|
||||
discrete_ranges.push(WithTokenSpan::new(
|
||||
drange,
|
||||
TokenSpan::new(start_token, end_token),
|
||||
));
|
||||
};
|
||||
|
||||
// Array element constraint
|
||||
|
|
@ -68,7 +74,15 @@ fn parse_composite_constraint(
|
|||
// Array constraint open
|
||||
Ok(None)
|
||||
} else {
|
||||
parse_discrete_range(ctx).map(Some)
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
let range = parse_discrete_range(ctx);
|
||||
let end_token = ctx.stream.get_last_token_id();
|
||||
range.map(|rng| {
|
||||
Some(WithTokenSpan::new(
|
||||
rng,
|
||||
TokenSpan::new(start_token, end_token),
|
||||
))
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -136,7 +150,7 @@ pub fn parse_subtype_constraint(
|
|||
pub fn parse_element_resolution_indication(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
) -> ParseResult<ResolutionIndication> {
|
||||
ctx.stream.expect_kind(LeftPar)?;
|
||||
let start_token = ctx.stream.expect_kind(LeftPar)?;
|
||||
|
||||
let first_ident = ctx.stream.expect_ident()?;
|
||||
|
||||
|
|
@ -181,8 +195,9 @@ pub fn parse_element_resolution_indication(
|
|||
);
|
||||
|
||||
}
|
||||
let last_token = ctx.stream.get_last_token_id();
|
||||
|
||||
ResolutionIndication::Record(element_resolutions)
|
||||
ResolutionIndication::Record(WithTokenSpan::new(element_resolutions, TokenSpan::new(start_token, last_token)))
|
||||
}
|
||||
))
|
||||
}
|
||||
|
|
@ -192,16 +207,16 @@ pub fn parse_subtype_indication(ctx: &mut ParsingContext<'_>) -> ParseResult<Sub
|
|||
if ctx.stream.peek_kind() == Some(LeftPar) {
|
||||
let resolution = parse_element_resolution_indication(ctx)?;
|
||||
let type_mark = parse_type_mark(ctx)?;
|
||||
(resolution, type_mark)
|
||||
(Some(resolution), type_mark)
|
||||
} else {
|
||||
let selected_name = parse_selected_name(ctx)?;
|
||||
match ctx.stream.peek_kind() {
|
||||
Some(Identifier) => (
|
||||
ResolutionIndication::FunctionName(selected_name),
|
||||
Some(ResolutionIndication::FunctionName(selected_name)),
|
||||
parse_type_mark(ctx)?,
|
||||
),
|
||||
_ => (
|
||||
ResolutionIndication::Unresolved,
|
||||
None,
|
||||
parse_type_mark_starting_with_name(ctx, selected_name)?,
|
||||
),
|
||||
}
|
||||
|
|
@ -229,7 +244,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("std_logic").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -242,7 +257,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::FunctionName(code.s1("resolve").name()),
|
||||
resolution: Some(ResolutionIndication::FunctionName(
|
||||
code.s1("resolve").name()
|
||||
)),
|
||||
type_mark: code.s1("std_logic").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -255,7 +272,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::ArrayElement(code.s1("resolve").name()),
|
||||
resolution: Some(ResolutionIndication::ArrayElement(
|
||||
code.s1("resolve").name()
|
||||
)),
|
||||
type_mark: code.s1("integer_vector").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -276,7 +295,10 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Record(vec![elem_resolution]),
|
||||
resolution: Some(ResolutionIndication::Record(WithTokenSpan::new(
|
||||
vec![elem_resolution],
|
||||
code.between("(", ")").token_span()
|
||||
))),
|
||||
type_mark: code.s1("rec_t").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -311,17 +333,20 @@ mod tests {
|
|||
|
||||
let elem3_resolution = RecordElementResolution {
|
||||
ident: code.s1("elem3").ident(),
|
||||
resolution: Box::new(ResolutionIndication::Record(vec![sub_elem_resolution])),
|
||||
resolution: Box::new(ResolutionIndication::Record(WithTokenSpan::new(
|
||||
vec![sub_elem_resolution],
|
||||
code.s1("(sub_elem sub_resolve)").token_span(),
|
||||
))),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Record(vec![
|
||||
elem1_resolution,
|
||||
elem2_resolution,
|
||||
elem3_resolution
|
||||
]),
|
||||
resolution: Some(ResolutionIndication::Record(WithTokenSpan::new(
|
||||
vec![elem1_resolution, elem2_resolution, elem3_resolution],
|
||||
code.s1("(elem1 (resolve1), elem2 resolve2, elem3 (sub_elem sub_resolve))")
|
||||
.token_span()
|
||||
))),
|
||||
type_mark: code.s1("rec_t").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -334,7 +359,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::FunctionName(code.s1("lib.foo.resolve").name()),
|
||||
resolution: Some(ResolutionIndication::FunctionName(
|
||||
code.s1("lib.foo.resolve").name()
|
||||
)),
|
||||
type_mark: code.s1("std_logic").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -348,7 +375,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("lib.foo.bar").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -367,7 +394,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -386,7 +413,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -398,14 +425,20 @@ mod tests {
|
|||
let code = Code::new("integer_vector(2-1 downto 0)");
|
||||
|
||||
let constraint = WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(vec![code.s1("2-1 downto 0").discrete_range()], None),
|
||||
SubtypeConstraint::Array(
|
||||
vec![WithTokenSpan::new(
|
||||
code.s1("2-1 downto 0").discrete_range(),
|
||||
code.s1("2-1 downto 0").token_span(),
|
||||
)],
|
||||
None,
|
||||
),
|
||||
code.s1("(2-1 downto 0)").token_span(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer_vector").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -417,14 +450,20 @@ mod tests {
|
|||
let code = Code::new("integer_vector(lib.foo.bar)");
|
||||
|
||||
let constraint = WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(vec![code.s1("lib.foo.bar").discrete_range()], None),
|
||||
SubtypeConstraint::Array(
|
||||
vec![WithTokenSpan::new(
|
||||
code.s1("lib.foo.bar").discrete_range(),
|
||||
code.s1("lib.foo.bar").token_span(),
|
||||
)],
|
||||
None,
|
||||
),
|
||||
code.s1("(lib.foo.bar)").token_span(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer_vector").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -436,14 +475,20 @@ mod tests {
|
|||
let code = Code::new("integer_vector(lib.pkg.bar'range)");
|
||||
|
||||
let constraint = WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(vec![code.s1("lib.pkg.bar'range").discrete_range()], None),
|
||||
SubtypeConstraint::Array(
|
||||
vec![WithTokenSpan::new(
|
||||
code.s1("lib.pkg.bar'range").discrete_range(),
|
||||
code.s1("lib.pkg.bar'range").token_span(),
|
||||
)],
|
||||
None,
|
||||
),
|
||||
code.s1("(lib.pkg.bar'range)").token_span(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer_vector").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -462,7 +507,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer_vector").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -476,8 +521,14 @@ mod tests {
|
|||
let constraint = WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(
|
||||
vec![
|
||||
code.s1("2-1 downto 0").discrete_range(),
|
||||
code.s1("11 to 14").discrete_range(),
|
||||
WithTokenSpan::new(
|
||||
code.s1("2-1 downto 0").discrete_range(),
|
||||
code.s1("2-1 downto 0").token_span(),
|
||||
),
|
||||
WithTokenSpan::new(
|
||||
code.s1("11 to 14").discrete_range(),
|
||||
code.s1("11 to 14").token_span(),
|
||||
),
|
||||
],
|
||||
None,
|
||||
),
|
||||
|
|
@ -487,7 +538,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer_vector").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -499,15 +550,27 @@ mod tests {
|
|||
let code = Code::new("integer_vector(2-1 downto 0, 11 to 14)(foo to bar)");
|
||||
|
||||
let element_constraint = WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(vec![code.s1("foo to bar").discrete_range()], None),
|
||||
SubtypeConstraint::Array(
|
||||
vec![WithTokenSpan::new(
|
||||
code.s1("foo to bar").discrete_range(),
|
||||
code.s1("foo to bar").token_span(),
|
||||
)],
|
||||
None,
|
||||
),
|
||||
code.s1("(foo to bar)").token_span(),
|
||||
);
|
||||
|
||||
let constraint = WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(
|
||||
vec![
|
||||
code.s1("2-1 downto 0").discrete_range(),
|
||||
code.s1("11 to 14").discrete_range(),
|
||||
WithTokenSpan::new(
|
||||
code.s1("2-1 downto 0").discrete_range(),
|
||||
code.s1("2-1 downto 0").token_span(),
|
||||
),
|
||||
WithTokenSpan::new(
|
||||
code.s1("11 to 14").discrete_range(),
|
||||
code.s1("11 to 14").token_span(),
|
||||
),
|
||||
],
|
||||
Some(Box::new(element_constraint)),
|
||||
),
|
||||
|
|
@ -517,7 +580,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("integer_vector").type_mark(),
|
||||
constraint: Some(constraint)
|
||||
}
|
||||
|
|
@ -531,7 +594,13 @@ mod tests {
|
|||
let tdata_constraint = ElementConstraint {
|
||||
ident: code.s1("tdata").ident(),
|
||||
constraint: Box::new(WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(vec![code.s1("2-1 downto 0").discrete_range()], None),
|
||||
SubtypeConstraint::Array(
|
||||
vec![WithTokenSpan::new(
|
||||
code.s1("2-1 downto 0").discrete_range(),
|
||||
code.s1("2-1 downto 0").token_span(),
|
||||
)],
|
||||
None,
|
||||
),
|
||||
code.s1("(2-1 downto 0)").token_span(),
|
||||
)),
|
||||
};
|
||||
|
|
@ -539,7 +608,13 @@ mod tests {
|
|||
let tuser_constraint = ElementConstraint {
|
||||
ident: code.s1("tuser").ident(),
|
||||
constraint: Box::new(WithTokenSpan::new(
|
||||
SubtypeConstraint::Array(vec![code.s1("3 to 5").discrete_range()], None),
|
||||
SubtypeConstraint::Array(
|
||||
vec![WithTokenSpan::new(
|
||||
code.s1("3 to 5").discrete_range(),
|
||||
code.s1("3 to 5").token_span(),
|
||||
)],
|
||||
None,
|
||||
),
|
||||
code.s1("(3 to 5)").token_span(),
|
||||
)),
|
||||
};
|
||||
|
|
@ -547,7 +622,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("axi_m2s_t").type_mark(),
|
||||
constraint: Some(WithTokenSpan::new(
|
||||
SubtypeConstraint::Record(vec![tdata_constraint, tuser_constraint]),
|
||||
|
|
@ -564,7 +639,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("obj'subtype").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
@ -575,7 +650,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.with_stream(parse_subtype_indication),
|
||||
SubtypeIndication {
|
||||
resolution: ResolutionIndication::Unresolved,
|
||||
resolution: None,
|
||||
type_mark: code.s1("obj.field'subtype").type_mark(),
|
||||
constraint: None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ use super::interface_declaration::{parse_generic, parse_parameter, parse_port};
|
|||
use super::names::{parse_association_list, parse_designator, parse_name, parse_type_mark};
|
||||
use super::object_declaration::{parse_file_declaration, parse_object_declaration};
|
||||
use super::range::{parse_discrete_range, parse_range};
|
||||
use super::separated_list::{parse_ident_list, parse_name_list};
|
||||
use super::sequential_statement::parse_sequential_statement;
|
||||
use super::subprogram::{
|
||||
parse_signature, parse_subprogram_declaration, parse_subprogram_specification,
|
||||
|
|
@ -126,15 +125,6 @@ impl CodeBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> SeparatedList<T> {
|
||||
pub fn single(item: T) -> SeparatedList<T> {
|
||||
SeparatedList {
|
||||
items: vec![item],
|
||||
tokens: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Code {
|
||||
pub symbols: Arc<Symbols>,
|
||||
|
|
@ -582,7 +572,7 @@ impl Code {
|
|||
pub fn character(&self) -> WithToken<u8> {
|
||||
self.parse_ok_no_diagnostics(|ctx: &mut ParsingContext| {
|
||||
let id = ctx.stream.expect_kind(Kind::Character)?;
|
||||
ctx.stream.get_token(id).to_character_value(id)
|
||||
ctx.stream.index(id).to_character_value(id)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -596,14 +586,6 @@ impl Code {
|
|||
self.parse_ok_no_diagnostics(parse_name)
|
||||
}
|
||||
|
||||
pub fn name_list(&self) -> SeparatedList<WithTokenSpan<Name>> {
|
||||
self.parse_ok_no_diagnostics(parse_name_list)
|
||||
}
|
||||
|
||||
pub fn ident_list(&self) -> SeparatedList<WithRef<Ident>> {
|
||||
self.parse_ok_no_diagnostics(parse_ident_list)
|
||||
}
|
||||
|
||||
pub fn type_mark(&self) -> WithTokenSpan<Name> {
|
||||
self.parse_ok_no_diagnostics(parse_type_mark)
|
||||
}
|
||||
|
|
@ -626,9 +608,7 @@ impl Code {
|
|||
}
|
||||
|
||||
pub fn file_decl(&self) -> FileDeclaration {
|
||||
self.parse_ok_no_diagnostics(parse_file_declaration)
|
||||
.remove(0)
|
||||
.item
|
||||
self.parse_ok_no_diagnostics(parse_file_declaration).item
|
||||
}
|
||||
|
||||
pub fn alias_decl(&self) -> AliasDeclaration {
|
||||
|
|
@ -676,7 +656,7 @@ impl Code {
|
|||
WithTokenSpan::from(
|
||||
CallOrIndexed {
|
||||
name,
|
||||
parameters: vec![],
|
||||
parameters: SeparatedList::default(),
|
||||
},
|
||||
span,
|
||||
)
|
||||
|
|
@ -719,7 +699,7 @@ impl Code {
|
|||
self.parse_ok_no_diagnostics(parse_waveform)
|
||||
}
|
||||
|
||||
pub fn aggregate(&self) -> WithTokenSpan<Vec<ElementAssociation>> {
|
||||
pub fn aggregate(&self) -> WithTokenSpan<Vec<WithTokenSpan<ElementAssociation>>> {
|
||||
self.parse_ok_no_diagnostics(parse_aggregate)
|
||||
}
|
||||
|
||||
|
|
@ -735,8 +715,8 @@ impl Code {
|
|||
self.parse_ok_no_diagnostics(parse_choices)
|
||||
}
|
||||
|
||||
pub fn use_clause(&self) -> UseClause {
|
||||
self.parse_ok_no_diagnostics(parse_use_clause).item
|
||||
pub fn use_clause(&self) -> WithTokenSpan<UseClause> {
|
||||
self.parse_ok_no_diagnostics(parse_use_clause)
|
||||
}
|
||||
|
||||
pub fn library_clause(&self) -> LibraryClause {
|
||||
|
|
@ -868,8 +848,8 @@ fn diagnostics_to_map(diagnostics: Vec<Diagnostic>) -> HashMap<Diagnostic, usize
|
|||
map
|
||||
}
|
||||
|
||||
// Drop releated info when we do not want to test for it
|
||||
pub fn without_releated(diagnostics: &[Diagnostic]) -> Vec<Diagnostic> {
|
||||
// Drop related info when we do not want to test for it
|
||||
pub fn without_related(diagnostics: &[Diagnostic]) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = diagnostics.to_vec();
|
||||
for diagnostic in diagnostics.iter_mut() {
|
||||
diagnostic.related.clear();
|
||||
|
|
@ -955,13 +935,10 @@ fn value_to_string(value: &Value) -> String {
|
|||
match value {
|
||||
Value::Identifier(ident) => ident.name_utf8(),
|
||||
Value::String(s) => String::from_utf8(s.chars().copied().collect_vec()).unwrap(),
|
||||
Value::BitString(_) => {
|
||||
panic!("value_to_string is currently not supported for BitString literals!")
|
||||
Value::BitString(s, ..) => String::from_utf8(s.chars().copied().collect_vec()).unwrap(),
|
||||
Value::AbstractLiteral(s, _) => {
|
||||
String::from_utf8(s.chars().copied().collect_vec()).unwrap()
|
||||
}
|
||||
Value::AbstractLiteral(lit) => match lit {
|
||||
AbstractLiteral::Integer(i) => i.to_string(),
|
||||
AbstractLiteral::Real(f) => f.to_string(),
|
||||
},
|
||||
Value::Character(char) => format!("'{}'", String::from_utf8(vec![*char]).unwrap()),
|
||||
Value::Text(text) => String::from_utf8(text.chars().copied().collect_vec()).unwrap(),
|
||||
Value::None => "".into(),
|
||||
|
|
|
|||
|
|
@ -412,8 +412,8 @@ pub fn kinds_str(kinds: &[Kind]) -> String {
|
|||
pub enum Value {
|
||||
Identifier(Symbol),
|
||||
String(Latin1String),
|
||||
BitString(ast::BitString),
|
||||
AbstractLiteral(ast::AbstractLiteral),
|
||||
BitString(Latin1String, ast::BitString),
|
||||
AbstractLiteral(Latin1String, ast::AbstractLiteral),
|
||||
Character(u8),
|
||||
// Raw text that is not processed (i.e. tokenized) further. Used in tool directives
|
||||
Text(Latin1String),
|
||||
|
|
@ -458,6 +458,28 @@ impl From<TokenId> for TokenSpan {
|
|||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for TokenId {
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
self.0 += rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<usize> for TokenId {
|
||||
type Output = TokenId;
|
||||
|
||||
fn sub(self, rhs: usize) -> Self::Output {
|
||||
TokenId(self.0 - rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for TokenId {
|
||||
type Output = TokenId;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
TokenId(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
/// AST elements for which it is necessary to get the underlying tokens can implement the `HasTokenSpan` trait.
|
||||
/// The trait provides getters for the start and end token.
|
||||
///
|
||||
|
|
@ -469,6 +491,8 @@ impl From<TokenId> for TokenSpan {
|
|||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// use vhdl_lang::ast::Name;
|
||||
/// use vhdl_lang::ast::token_range::WithTokenSpan;
|
||||
/// use vhdl_lang_macros::{with_token_span, TokenSpan};
|
||||
///
|
||||
/// // With `with_token_span` a field `info` of type `(TokenId, TokenId)` is inserted.
|
||||
|
|
@ -476,19 +500,19 @@ impl From<TokenId> for TokenSpan {
|
|||
/// #[with_token_span]
|
||||
/// #[derive(PartialEq, Debug, Clone)]
|
||||
/// pub struct UseClause {
|
||||
/// pub name_list: ::vhdl_lang::ast::NameList,
|
||||
/// pub name_list: Vec<WithTokenSpan<Name>>,
|
||||
/// }
|
||||
///
|
||||
/// #[with_token_span]
|
||||
/// #[derive(PartialEq, Debug, Clone)]
|
||||
/// pub struct ContextReference {
|
||||
/// pub name_list: ::vhdl_lang::ast::NameList,
|
||||
/// pub name_list: Vec<WithTokenSpan<Name>>,
|
||||
/// }
|
||||
///
|
||||
/// #[with_token_span]
|
||||
/// #[derive(PartialEq, Debug, Clone)]
|
||||
/// pub struct LibraryClause {
|
||||
/// pub name_list: ::vhdl_lang::ast::IdentList,
|
||||
/// pub name_list: Vec<::vhdl_lang::ast::WithRef<::vhdl_lang::ast::Ident>>,
|
||||
/// }
|
||||
///
|
||||
/// // Enums can use the `TokenSpan` derive macro directly
|
||||
|
|
@ -543,7 +567,12 @@ impl Debug for TokenSpan {
|
|||
|
||||
impl TokenSpan {
|
||||
pub fn new(start_token: TokenId, end_token: TokenId) -> Self {
|
||||
debug_assert!(start_token <= end_token);
|
||||
debug_assert!(
|
||||
start_token <= end_token,
|
||||
"start token {:} is past end token {}",
|
||||
start_token.0,
|
||||
end_token.0
|
||||
);
|
||||
Self {
|
||||
start_token,
|
||||
end_token,
|
||||
|
|
@ -601,6 +630,65 @@ impl TokenSpan {
|
|||
}
|
||||
}
|
||||
|
||||
struct TokenSpanIterator {
|
||||
end: TokenId,
|
||||
current: TokenId,
|
||||
}
|
||||
|
||||
impl Iterator for TokenSpanIterator {
|
||||
type Item = TokenId;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let old_current = self.current;
|
||||
// Note: in this example, current may point to an
|
||||
// invalid token (as it is greater than and).
|
||||
// This is OK because the invalid ID is never returned.
|
||||
if self.current > self.end {
|
||||
None
|
||||
} else {
|
||||
self.current += 1;
|
||||
Some(old_current)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_iterator() {
|
||||
let span = TokenSpan {
|
||||
start_token: TokenId(0),
|
||||
end_token: TokenId(0),
|
||||
};
|
||||
let mut itr = span.iter();
|
||||
assert_eq!(itr.next(), Some(TokenId(0)));
|
||||
assert_eq!(itr.next(), None);
|
||||
|
||||
let span = TokenSpan {
|
||||
start_token: TokenId(0),
|
||||
end_token: TokenId(1),
|
||||
};
|
||||
let mut itr = span.iter();
|
||||
assert_eq!(itr.next(), Some(TokenId(0)));
|
||||
assert_eq!(itr.next(), Some(TokenId(1)));
|
||||
assert_eq!(itr.next(), None);
|
||||
}
|
||||
|
||||
impl TokenSpan {
|
||||
pub fn iter(&self) -> impl Iterator<Item = TokenId> {
|
||||
TokenSpanIterator {
|
||||
current: self.start_token,
|
||||
end: self.end_token,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.end_token.0 - self.start_token.0 + 1
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that conforms to `TokenAccess` can be indexed using a `TokenId`.
|
||||
/// Convenience methods exist to directly get the `SrcPos` for a given `TokenId`
|
||||
/// or a span starting at a certain token and ending at another.
|
||||
|
|
@ -608,14 +696,16 @@ impl TokenSpan {
|
|||
/// Types such as `Vec` and `array` implement `TokenAccess`
|
||||
pub trait TokenAccess {
|
||||
/// Get a token by its ID
|
||||
fn get_token(&self, id: TokenId) -> &Token;
|
||||
fn get_token(&self, id: TokenId) -> Option<&Token>;
|
||||
|
||||
fn index(&self, id: TokenId) -> &Token;
|
||||
|
||||
/// Get a slice of tokens by using a start ID and an end ID
|
||||
fn get_token_slice(&self, start_id: TokenId, end_id: TokenId) -> &[Token];
|
||||
|
||||
/// Get a token's position by its ID
|
||||
fn get_pos(&self, id: TokenId) -> &SrcPos {
|
||||
&self.get_token(id).pos
|
||||
&self.index(id).pos
|
||||
}
|
||||
|
||||
/// Get a span where the beginning of that span is the beginning of the token indexed by
|
||||
|
|
@ -626,7 +716,11 @@ pub trait TokenAccess {
|
|||
}
|
||||
|
||||
impl TokenAccess for Vec<Token> {
|
||||
fn get_token(&self, id: TokenId) -> &Token {
|
||||
fn get_token(&self, id: TokenId) -> Option<&Token> {
|
||||
self.get(id.0)
|
||||
}
|
||||
|
||||
fn index(&self, id: TokenId) -> &Token {
|
||||
&self[id.0]
|
||||
}
|
||||
|
||||
|
|
@ -636,7 +730,11 @@ impl TokenAccess for Vec<Token> {
|
|||
}
|
||||
|
||||
impl TokenAccess for [Token] {
|
||||
fn get_token(&self, id: TokenId) -> &Token {
|
||||
fn get_token(&self, id: TokenId) -> Option<&Token> {
|
||||
self.get(id.0)
|
||||
}
|
||||
|
||||
fn index(&self, id: TokenId) -> &Token {
|
||||
&self[id.0]
|
||||
}
|
||||
|
||||
|
|
@ -661,6 +759,7 @@ pub struct Comment {
|
|||
use crate::standard::VHDLStandard;
|
||||
use std::convert::AsRef;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::ops::{Add, AddAssign, Sub};
|
||||
use strum::IntoStaticStr;
|
||||
|
||||
impl AsRef<SrcPos> for Token {
|
||||
|
|
@ -716,7 +815,7 @@ impl Token {
|
|||
pub fn to_bit_string(&self, id: TokenId) -> DiagnosticResult<WithToken<ast::BitString>> {
|
||||
if let Token {
|
||||
kind: BitString,
|
||||
value: Value::BitString(value),
|
||||
value: Value::BitString(_, value),
|
||||
..
|
||||
} = self
|
||||
{
|
||||
|
|
@ -732,7 +831,7 @@ impl Token {
|
|||
) -> DiagnosticResult<WithToken<ast::AbstractLiteral>> {
|
||||
if let Token {
|
||||
kind: AbstractLiteral,
|
||||
value: Value::AbstractLiteral(value),
|
||||
value: Value::AbstractLiteral(_, value),
|
||||
..
|
||||
} = self
|
||||
{
|
||||
|
|
@ -766,6 +865,28 @@ impl Token {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the full range of this token, respecting any potential comments.
|
||||
/// Note that [Token::pos] only returns the position of the token itself.
|
||||
pub fn full_range(&self) -> crate::data::Range {
|
||||
let mut range = self.pos.range();
|
||||
if let Some(comments) = &self.comments {
|
||||
if let Some(comment) = comments.leading.first() {
|
||||
range.start = comment.range.start
|
||||
}
|
||||
if let Some(trailing) = &comments.trailing {
|
||||
range.end = trailing.range.end
|
||||
}
|
||||
}
|
||||
range
|
||||
}
|
||||
|
||||
/// return `true` when `self` is equal to `other` while ignoring all
|
||||
/// changes that are attributed to their position in the source file
|
||||
/// and all changes that only affect comments.
|
||||
pub fn equal_format(&self, other: &Token) -> bool {
|
||||
self.kind == other.kind && self.value == other.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
|
|
@ -857,8 +978,9 @@ fn parse_integer(
|
|||
reader: &mut ContentReader,
|
||||
base: u64,
|
||||
stop_on_suffix: bool,
|
||||
) -> Result<u64, TokenError> {
|
||||
) -> Result<(u64, Latin1String), TokenError> {
|
||||
let mut result = Some(0_u64);
|
||||
let mut result_str = Latin1String::empty();
|
||||
let mut too_large_digit = None;
|
||||
let mut invalid_character = None;
|
||||
|
||||
|
|
@ -879,10 +1001,12 @@ fn parse_integer(
|
|||
b'a'..=b'f' => 10 + b - b'a',
|
||||
b'A'..=b'F' => 10 + b - b'A',
|
||||
b'_' => {
|
||||
result_str.push(b);
|
||||
reader.skip();
|
||||
continue;
|
||||
}
|
||||
b'g'..=b'z' | b'G'..=b'Z' => {
|
||||
result_str.push(b);
|
||||
invalid_character = Some((b, reader.pos()));
|
||||
reader.skip();
|
||||
continue;
|
||||
|
|
@ -896,6 +1020,7 @@ fn parse_integer(
|
|||
too_large_digit = Some((b, reader.pos()));
|
||||
}
|
||||
|
||||
result_str.push(b);
|
||||
reader.skip();
|
||||
|
||||
result = result
|
||||
|
|
@ -918,7 +1043,7 @@ fn parse_integer(
|
|||
),
|
||||
))
|
||||
} else if let Some(result) = result {
|
||||
Ok(result)
|
||||
Ok((result, result_str))
|
||||
} else {
|
||||
Err(TokenError::range(
|
||||
start,
|
||||
|
|
@ -928,25 +1053,30 @@ fn parse_integer(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_exponent(reader: &mut ContentReader) -> Result<i32, TokenError> {
|
||||
fn parse_exponent(reader: &mut ContentReader) -> Result<(i32, Latin1String), TokenError> {
|
||||
let start = reader.pos();
|
||||
let mut buffer = Latin1String::empty();
|
||||
let negative = {
|
||||
if reader.peek()? == Some(b'-') {
|
||||
buffer.push(b'-');
|
||||
reader.skip();
|
||||
true
|
||||
} else {
|
||||
reader.skip_if(b'+')?;
|
||||
if reader.skip_if(b'+')? {
|
||||
buffer.push(b'+');
|
||||
}
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
let exp = parse_integer(reader, 10, false)?;
|
||||
let (exp, mut exp_name) = parse_integer(reader, 10, false)?;
|
||||
buffer.append(&mut exp_name);
|
||||
if negative {
|
||||
if exp <= (-(i32::MIN as i64)) as u64 {
|
||||
return Ok((-(exp as i64)) as i32);
|
||||
return Ok(((-(exp as i64)) as i32, buffer));
|
||||
}
|
||||
} else if exp <= i32::MAX as u64 {
|
||||
return Ok(exp as i32);
|
||||
return Ok((exp as i32, buffer));
|
||||
}
|
||||
|
||||
Err(TokenError::range(
|
||||
|
|
@ -1084,8 +1214,9 @@ fn parse_multi_line_comment(reader: &mut ContentReader) -> Result<Comment, Token
|
|||
fn parse_real_literal(
|
||||
buffer: &mut Latin1String,
|
||||
reader: &mut ContentReader,
|
||||
) -> Result<f64, TokenError> {
|
||||
buffer.bytes.clear();
|
||||
) -> Result<(f64, Latin1String), TokenError> {
|
||||
buffer.clear();
|
||||
let mut text = Latin1String::empty();
|
||||
let start = reader.pos();
|
||||
while let Some(b) = reader.peek_lowercase()? {
|
||||
match b {
|
||||
|
|
@ -1096,10 +1227,12 @@ fn parse_real_literal(
|
|||
break;
|
||||
}
|
||||
b'0'..=b'9' | b'a'..=b'd' | b'f' | b'A'..=b'F' | b'.' => {
|
||||
text.push(b);
|
||||
reader.skip();
|
||||
buffer.bytes.push(b);
|
||||
buffer.push(b);
|
||||
}
|
||||
b'_' => {
|
||||
text.push(b);
|
||||
reader.skip();
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1111,15 +1244,16 @@ fn parse_real_literal(
|
|||
|
||||
let string = unsafe { std::str::from_utf8_unchecked(&buffer.bytes) };
|
||||
|
||||
let result: Result<f64, TokenError> =
|
||||
string.parse().map_err(|err: std::num::ParseFloatError| {
|
||||
string
|
||||
.parse::<f64>()
|
||||
.map(|val| (val, text))
|
||||
.map_err(|err: std::num::ParseFloatError| {
|
||||
TokenError::range(start, reader.pos(), err.to_string())
|
||||
});
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
fn exponentiate(value: u64, exp: u32) -> Option<u64> {
|
||||
(10_u64).checked_pow(exp).and_then(|x| x.checked_mul(value))
|
||||
10_u64.checked_pow(exp).and_then(|x| x.checked_mul(value))
|
||||
}
|
||||
|
||||
/// LRM 15.5 Abstract literals
|
||||
|
|
@ -1135,37 +1269,44 @@ fn parse_abstract_literal(
|
|||
// Real
|
||||
Some(b'.') => {
|
||||
reader.set_state(state);
|
||||
let real = parse_real_literal(buffer, reader)?;
|
||||
let (real, mut text) = parse_real_literal(buffer, reader)?;
|
||||
|
||||
match reader.peek()? {
|
||||
// Exponent
|
||||
Some(b'e') | Some(b'E') => {
|
||||
text.push(reader.peek().unwrap().unwrap());
|
||||
reader.skip();
|
||||
let exp = parse_exponent(reader)?;
|
||||
let (exp, mut exp_text) = parse_exponent(reader)?;
|
||||
text.append(&mut exp_text);
|
||||
Ok((
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(
|
||||
real * (10.0_f64).powi(exp),
|
||||
)),
|
||||
Value::AbstractLiteral(
|
||||
text,
|
||||
ast::AbstractLiteral::Real(real * 10_f64.powi(exp)),
|
||||
),
|
||||
))
|
||||
}
|
||||
_ => Ok((
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(real)),
|
||||
Value::AbstractLiteral(text, ast::AbstractLiteral::Real(real)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
// Integer exponent
|
||||
Some(b'e') => {
|
||||
let integer = initial?;
|
||||
let mut text = Latin1String::empty();
|
||||
let (integer, mut int_text) = initial?;
|
||||
text.append(&mut int_text);
|
||||
text.push(reader.peek().unwrap().unwrap());
|
||||
reader.skip();
|
||||
let exp = parse_exponent(reader)?;
|
||||
let (exp, mut exp_text) = parse_exponent(reader)?;
|
||||
text.append(&mut exp_text);
|
||||
if exp >= 0 {
|
||||
if let Some(value) = exponentiate(integer, exp as u32) {
|
||||
Ok((
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(value)),
|
||||
Value::AbstractLiteral(text, ast::AbstractLiteral::Integer(value)),
|
||||
))
|
||||
} else {
|
||||
Err(TokenError::range(
|
||||
|
|
@ -1185,17 +1326,20 @@ fn parse_abstract_literal(
|
|||
|
||||
// Based integer
|
||||
Some(b'#') => {
|
||||
let base = initial?;
|
||||
let (base, mut base_text) = initial?;
|
||||
base_text.push(b'#');
|
||||
reader.skip();
|
||||
let base_result = parse_integer(reader, base, false);
|
||||
|
||||
if let Some(b'#') = reader.peek()? {
|
||||
reader.skip();
|
||||
let integer = base_result?;
|
||||
let (integer, mut int_text) = base_result?;
|
||||
base_text.append(&mut int_text);
|
||||
base_text.push(b'#');
|
||||
if (2..=16).contains(&base) {
|
||||
Ok((
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(integer)),
|
||||
Value::AbstractLiteral(base_text, ast::AbstractLiteral::Integer(integer)),
|
||||
))
|
||||
} else {
|
||||
Err(TokenError::range(
|
||||
|
|
@ -1215,11 +1359,16 @@ fn parse_abstract_literal(
|
|||
|
||||
// Bit string literal
|
||||
Some(b's') | Some(b'u') | Some(b'b') | Some(b'o') | Some(b'x') | Some(b'd') => {
|
||||
let integer = initial?;
|
||||
let (integer, _) = initial?;
|
||||
|
||||
if let Some(base_spec) = parse_base_specifier(reader)? {
|
||||
// @TODO check overflow
|
||||
parse_bit_string(buffer, reader, base_spec, Some(integer as u32))
|
||||
parse_bit_string(
|
||||
buffer,
|
||||
reader,
|
||||
base_spec,
|
||||
Some(integer as u32),
|
||||
state.pos().character as usize,
|
||||
)
|
||||
} else {
|
||||
Err(TokenError::range(
|
||||
state.pos(),
|
||||
|
|
@ -1230,9 +1379,10 @@ fn parse_abstract_literal(
|
|||
}
|
||||
_ => {
|
||||
// Plain integer
|
||||
let (integer, integer_text) = initial?;
|
||||
Ok((
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(initial?)),
|
||||
Value::AbstractLiteral(integer_text, ast::AbstractLiteral::Integer(integer)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -1285,6 +1435,7 @@ fn parse_bit_string(
|
|||
reader: &mut ContentReader,
|
||||
base_specifier: BaseSpecifier,
|
||||
bit_string_length: Option<u32>,
|
||||
start: usize,
|
||||
) -> Result<(Kind, Value), TokenError> {
|
||||
let value = match parse_string(buffer, reader) {
|
||||
Ok(value) => value,
|
||||
|
|
@ -1294,13 +1445,21 @@ fn parse_bit_string(
|
|||
}
|
||||
};
|
||||
|
||||
let end_pos = reader.state().pos();
|
||||
let actual_value = reader
|
||||
.value_at(end_pos.line as usize, start, end_pos.character as usize)
|
||||
.unwrap();
|
||||
|
||||
Ok((
|
||||
BitString,
|
||||
Value::BitString(ast::BitString {
|
||||
length: bit_string_length,
|
||||
base: base_specifier,
|
||||
value,
|
||||
}),
|
||||
Value::BitString(
|
||||
actual_value,
|
||||
ast::BitString {
|
||||
length: bit_string_length,
|
||||
base: base_specifier,
|
||||
value,
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -1560,8 +1719,15 @@ impl<'a> Tokenizer<'a> {
|
|||
|
||||
let (kind, value) = match byte {
|
||||
b'a'..=b'z' | b'A'..=b'Z' => {
|
||||
let state = self.reader.state();
|
||||
if let Some(base_spec) = maybe_base_specifier(&mut self.reader)? {
|
||||
parse_bit_string(&mut self.buffer, &mut self.reader, base_spec, None)?
|
||||
parse_bit_string(
|
||||
&mut self.buffer,
|
||||
&mut self.reader,
|
||||
base_spec,
|
||||
None,
|
||||
state.pos().character as usize,
|
||||
)?
|
||||
} else {
|
||||
parse_basic_identifier_or_keyword(
|
||||
&mut self.buffer,
|
||||
|
|
@ -1805,7 +1971,7 @@ impl<'a> Tokenizer<'a> {
|
|||
Err(err) => {
|
||||
self.state.start = self.reader.state();
|
||||
Err(Diagnostic::syntax_error(
|
||||
&self.source.pos(err.range.start, err.range.end),
|
||||
self.source.pos(err.range.start, err.range.end),
|
||||
err.message,
|
||||
))
|
||||
}
|
||||
|
|
@ -1822,7 +1988,7 @@ impl<'a> Tokenizer<'a> {
|
|||
if let Err(err) = read_until_newline(&mut self.buffer, &mut self.reader) {
|
||||
self.state.start = self.reader.state();
|
||||
return Err(Diagnostic::syntax_error(
|
||||
&self.source.pos(err.range.start, err.range.end),
|
||||
self.source.pos(err.range.start, err.range.end),
|
||||
err.message,
|
||||
));
|
||||
}
|
||||
|
|
@ -2014,24 +2180,39 @@ my_other_ident",
|
|||
vec![
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(100))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"100"),
|
||||
ast::AbstractLiteral::Integer(100)
|
||||
)
|
||||
),
|
||||
(Minus, Value::None),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(123))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"123"),
|
||||
ast::AbstractLiteral::Integer(123)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(162))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"1_6_2"),
|
||||
ast::AbstractLiteral::Integer(162)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(1000))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"1e3"),
|
||||
ast::AbstractLiteral::Integer(1000)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(20000))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"2E4"),
|
||||
ast::AbstractLiteral::Integer(20000)
|
||||
)
|
||||
),
|
||||
]
|
||||
);
|
||||
|
|
@ -2047,11 +2228,11 @@ my_other_ident",
|
|||
tokens,
|
||||
vec![
|
||||
Err(Diagnostic::syntax_error(
|
||||
&code.s1("€"),
|
||||
code.s1("€"),
|
||||
"Found invalid latin-1 character '€'",
|
||||
)),
|
||||
Err(Diagnostic::syntax_error(
|
||||
&code.s1("\u{1F4A3}"),
|
||||
code.s1("\u{1F4A3}"),
|
||||
"Found invalid latin-1 character '\u{1F4A3}'",
|
||||
)),
|
||||
]
|
||||
|
|
@ -2066,7 +2247,7 @@ my_other_ident",
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Integer literals may not have negative exponent",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2079,32 +2260,53 @@ my_other_ident",
|
|||
vec![
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(0.1))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"0.1"),
|
||||
ast::AbstractLiteral::Real(0.1)
|
||||
)
|
||||
),
|
||||
(Minus, Value::None),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(22.33))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"2_2.3_3"),
|
||||
ast::AbstractLiteral::Real(22.33)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(2000.0))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"2.0e3"),
|
||||
ast::AbstractLiteral::Real(2000.0)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(333.0))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"3.33E2"),
|
||||
ast::AbstractLiteral::Real(333.0)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(0.021))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"2.1e-2"),
|
||||
ast::AbstractLiteral::Real(0.021)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(44.0))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"4.4e+1"),
|
||||
ast::AbstractLiteral::Real(44.0)
|
||||
)
|
||||
),
|
||||
(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(2500.0))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"2.5E+3"),
|
||||
ast::AbstractLiteral::Real(2500.0)
|
||||
)
|
||||
),
|
||||
]
|
||||
);
|
||||
|
|
@ -2116,7 +2318,10 @@ my_other_ident",
|
|||
kind_value_tokenize("0.1000_0000_0000_0000_0000_0000_0000_0000"),
|
||||
vec![(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(1e-1))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"0.1000_0000_0000_0000_0000_0000_0000_0000"),
|
||||
ast::AbstractLiteral::Real(1e-1)
|
||||
)
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
|
@ -2127,7 +2332,10 @@ my_other_ident",
|
|||
kind_value_tokenize("1000_0000_0000_0000_0000_0000_0000_0000.0"),
|
||||
vec![(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(1e31))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"1000_0000_0000_0000_0000_0000_0000_0000.0"),
|
||||
ast::AbstractLiteral::Real(1e31)
|
||||
)
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
|
@ -2139,7 +2347,10 @@ my_other_ident",
|
|||
kind_value_tokenize("2.71828182845904523536"),
|
||||
vec![(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Real(2.718_281_828_459_045))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"2.71828182845904523536"),
|
||||
ast::AbstractLiteral::Real(2.718_281_828_459_045)
|
||||
)
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
|
@ -2205,7 +2416,7 @@ my_other_ident",
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Multi line string"
|
||||
))]
|
||||
);
|
||||
|
|
@ -2219,7 +2430,7 @@ my_other_ident",
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Reached EOF before end quote",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2254,13 +2465,11 @@ my_other_ident",
|
|||
("".to_owned(), None)
|
||||
};
|
||||
|
||||
let code = format!("{length_str}{base_spec}\"{value}\"");
|
||||
let mut code = format!("{length_str}{base_spec}\"{value}\"");
|
||||
|
||||
let code = if upper_case {
|
||||
code.to_ascii_uppercase()
|
||||
} else {
|
||||
code
|
||||
};
|
||||
if upper_case {
|
||||
code.make_ascii_uppercase()
|
||||
}
|
||||
|
||||
let value = if upper_case {
|
||||
value.to_ascii_uppercase()
|
||||
|
|
@ -2268,17 +2477,22 @@ my_other_ident",
|
|||
value.to_owned()
|
||||
};
|
||||
|
||||
let original_code = code.clone();
|
||||
|
||||
let code = Code::new(code.as_str());
|
||||
let tokens = code.tokenize();
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec![Token {
|
||||
kind: BitString,
|
||||
value: Value::BitString(ast::BitString {
|
||||
length: length_opt,
|
||||
base,
|
||||
value: Latin1String::from_utf8_unchecked(value.as_str()),
|
||||
}),
|
||||
value: Value::BitString(
|
||||
Latin1String::from_utf8_unchecked(original_code.as_str()),
|
||||
ast::BitString {
|
||||
length: length_opt,
|
||||
base,
|
||||
value: Latin1String::from_utf8_unchecked(value.as_str()),
|
||||
}
|
||||
),
|
||||
pos: code.pos(),
|
||||
comments: None,
|
||||
},]
|
||||
|
|
@ -2295,7 +2509,7 @@ my_other_ident",
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Invalid bit string literal",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2305,7 +2519,7 @@ my_other_ident",
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Invalid bit string literal",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2317,21 +2531,30 @@ my_other_ident",
|
|||
kind_value_tokenize("2#101#"),
|
||||
vec![(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(5))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"2#101#"),
|
||||
ast::AbstractLiteral::Integer(5)
|
||||
)
|
||||
),]
|
||||
);
|
||||
assert_eq!(
|
||||
kind_value_tokenize("8#321#"),
|
||||
vec![(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(3 * 8 * 8 + 2 * 8 + 1))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"8#321#"),
|
||||
ast::AbstractLiteral::Integer(3 * 8 * 8 + 2 * 8 + 1)
|
||||
)
|
||||
),]
|
||||
);
|
||||
assert_eq!(
|
||||
kind_value_tokenize("16#eEFfa#"),
|
||||
vec![(
|
||||
AbstractLiteral,
|
||||
Value::AbstractLiteral(ast::AbstractLiteral::Integer(0xeeffa))
|
||||
Value::AbstractLiteral(
|
||||
Latin1String::new(b"16#eEFfa#"),
|
||||
ast::AbstractLiteral::Integer(0xeeffa)
|
||||
)
|
||||
),]
|
||||
);
|
||||
}
|
||||
|
|
@ -2344,7 +2567,7 @@ my_other_ident",
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s1("k"),
|
||||
code.s1("k"),
|
||||
"Invalid integer character 'k'",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2352,13 +2575,24 @@ my_other_ident",
|
|||
|
||||
#[test]
|
||||
fn tokenize_illegal_based_integer() {
|
||||
// May not use digit larger than or equal base
|
||||
let code = Code::new("3#3#");
|
||||
let (tokens, _) = code.tokenize_result();
|
||||
println!("{:?}", tokens);
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
code.s("3", 2),
|
||||
"Illegal digit '3' for base 3",
|
||||
))]
|
||||
);
|
||||
// Base may only be 2-16
|
||||
let code = Code::new("1#0#");
|
||||
let (tokens, _) = code.tokenize_result();
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s1("1"),
|
||||
code.s1("1"),
|
||||
"Base must be at least 2 and at most 16, got 1",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2367,26 +2601,16 @@ my_other_ident",
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s1("17"),
|
||||
code.s1("17"),
|
||||
"Base must be at least 2 and at most 16, got 17",
|
||||
))]
|
||||
);
|
||||
// May not use digit larger than or equal base
|
||||
let code = Code::new("3#3#");
|
||||
let (tokens, _) = code.tokenize_result();
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s("3", 2),
|
||||
"Illegal digit '3' for base 3",
|
||||
))]
|
||||
);
|
||||
let code = Code::new("15#f#");
|
||||
let (tokens, _) = code.tokenize_result();
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s1("f"),
|
||||
code.s1("f"),
|
||||
"Illegal digit 'f' for base 15",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2565,7 +2789,7 @@ comment
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Integer too large for 64-bit unsigned",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2576,7 +2800,7 @@ comment
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Integer too large for 64-bit unsigned",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2588,7 +2812,7 @@ comment
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s1(&exponent_str),
|
||||
code.s1(&exponent_str),
|
||||
"Exponent too large for 32-bits signed",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2600,7 +2824,7 @@ comment
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s1(&exponent_str),
|
||||
code.s1(&exponent_str),
|
||||
"Exponent too large for 32-bits signed",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2611,7 +2835,7 @@ comment
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.pos(),
|
||||
code.pos(),
|
||||
"Integer too large for 64-bit unsigned",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2623,7 +2847,10 @@ comment
|
|||
tokens,
|
||||
vec![Ok(Token {
|
||||
kind: AbstractLiteral,
|
||||
value: Value::AbstractLiteral(ast::AbstractLiteral::Integer(u64::MAX)),
|
||||
value: Value::AbstractLiteral(
|
||||
Latin1String::from_utf8_unchecked(&u64::MAX.to_string()),
|
||||
ast::AbstractLiteral::Integer(u64::MAX)
|
||||
),
|
||||
pos: code.pos(),
|
||||
comments: None,
|
||||
})]
|
||||
|
|
@ -2643,7 +2870,7 @@ comment
|
|||
pos: code.s1("begin").pos(),
|
||||
comments: None,
|
||||
}),
|
||||
Err(Diagnostic::syntax_error(&code.s1("!"), "Illegal token")),
|
||||
Err(Diagnostic::syntax_error(code.s1("!"), "Illegal token")),
|
||||
Ok(Token {
|
||||
kind: End,
|
||||
value: Value::None,
|
||||
|
|
@ -2676,7 +2903,7 @@ comment
|
|||
assert_eq!(
|
||||
tokens,
|
||||
vec![Err(Diagnostic::syntax_error(
|
||||
&code.s1("/* final"),
|
||||
code.s1("/* final"),
|
||||
"Incomplete multi-line comment",
|
||||
))]
|
||||
);
|
||||
|
|
@ -2783,7 +3010,10 @@ bar*/
|
|||
tokens,
|
||||
vec![Ok(Token {
|
||||
kind: AbstractLiteral,
|
||||
value: Value::AbstractLiteral(ast::AbstractLiteral::Integer(2)),
|
||||
value: Value::AbstractLiteral(
|
||||
Latin1String::new(b"2"),
|
||||
ast::AbstractLiteral::Integer(2)
|
||||
),
|
||||
pos: code.s1("2").pos(),
|
||||
comments: Some(Box::new(TokenComments {
|
||||
leading: vec![Comment {
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ impl<'a> TokenStream<'a> {
|
|||
|
||||
pub fn pop_optional_ident(&self) -> Option<Ident> {
|
||||
self.pop_if_kind(Identifier)
|
||||
.map(|id| self.get_token(id).to_identifier_value(id).unwrap())
|
||||
.map(|id| self.index(id).to_identifier_value(id).unwrap())
|
||||
}
|
||||
|
||||
pub fn expect_ident(&self) -> DiagnosticResult<Ident> {
|
||||
|
|
@ -290,10 +290,14 @@ impl<'a> TokenStream<'a> {
|
|||
}
|
||||
|
||||
impl<'a> TokenAccess for TokenStream<'a> {
|
||||
fn get_token(&self, id: TokenId) -> &Token {
|
||||
fn get_token(&self, id: TokenId) -> Option<&Token> {
|
||||
self.tokens[self.token_offset.get()..].get_token(id)
|
||||
}
|
||||
|
||||
fn index(&self, id: TokenId) -> &Token {
|
||||
self.tokens[self.token_offset.get()..].index(id)
|
||||
}
|
||||
|
||||
fn get_token_slice(&self, start_id: TokenId, end_id: TokenId) -> &[Token] {
|
||||
self.tokens[self.token_offset.get()..].get_token_slice(start_id, end_id)
|
||||
}
|
||||
|
|
@ -558,12 +562,12 @@ end arch;
|
|||
let tokens = code.tokenize();
|
||||
assert_eq!(
|
||||
tokens[0],
|
||||
stream.get_token(stream.get_current_token_id()).clone()
|
||||
stream.index(stream.get_current_token_id()).clone()
|
||||
);
|
||||
stream.skip();
|
||||
assert_eq!(
|
||||
tokens[1],
|
||||
stream.get_token(stream.get_current_token_id()).clone()
|
||||
stream.index(stream.get_current_token_id()).clone()
|
||||
);
|
||||
stream
|
||||
.skip_until(|kind| kind == SemiColon)
|
||||
|
|
@ -571,12 +575,12 @@ end arch;
|
|||
stream.slice_tokens();
|
||||
assert_eq!(
|
||||
tokens[3],
|
||||
stream.get_token(stream.get_current_token_id()).clone()
|
||||
stream.index(stream.get_current_token_id()).clone()
|
||||
);
|
||||
stream.skip();
|
||||
assert_eq!(
|
||||
tokens[4],
|
||||
stream.get_token(stream.get_current_token_id()).clone()
|
||||
stream.index(stream.get_current_token_id()).clone()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use super::range::{parse_array_index_constraint, parse_range};
|
|||
use super::subprogram::parse_subprogram_declaration;
|
||||
use super::subtype_indication::parse_subtype_indication;
|
||||
use super::tokens::{Kind::*, TokenSpan};
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::*;
|
||||
use crate::ast::{AbstractLiteral, Range};
|
||||
use crate::named_entity::Reference;
|
||||
|
|
@ -19,6 +20,7 @@ use crate::syntax::names::parse_type_mark;
|
|||
use crate::syntax::recover::{expect_semicolon, expect_semicolon_or_last};
|
||||
use itertools::Itertools;
|
||||
use vhdl_lang::syntax::parser::ParsingContext;
|
||||
use vhdl_lang::TokenId;
|
||||
|
||||
/// LRM 5.2.2 Enumeration types
|
||||
fn parse_enumeration_type_definition(ctx: &mut ParsingContext<'_>) -> ParseResult<TypeDefinition> {
|
||||
|
|
@ -64,9 +66,13 @@ fn parse_array_index_constraints(ctx: &mut ParsingContext<'_>) -> ParseResult<Ve
|
|||
/// LRM 5.3.2 Array types
|
||||
fn parse_array_type_definition(ctx: &mut ParsingContext<'_>) -> ParseResult<TypeDefinition> {
|
||||
let index_constraints = parse_array_index_constraints(ctx)?;
|
||||
ctx.stream.expect_kind(Of)?;
|
||||
let of_token = ctx.stream.expect_kind(Of)?;
|
||||
let element_subtype = parse_subtype_indication(ctx)?;
|
||||
Ok(TypeDefinition::Array(index_constraints, element_subtype))
|
||||
Ok(TypeDefinition::Array(
|
||||
index_constraints,
|
||||
of_token,
|
||||
element_subtype,
|
||||
))
|
||||
}
|
||||
|
||||
/// LRM 5.3.3 Record types
|
||||
|
|
@ -88,12 +94,13 @@ fn parse_record_type_definition(
|
|||
.into_iter()
|
||||
.map(WithDecl::new)
|
||||
.collect_vec();
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let subtype = parse_subtype_indication(ctx)?;
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
elem_decls.push(ElementDeclaration {
|
||||
idents,
|
||||
subtype: subtype.clone(),
|
||||
colon_token,
|
||||
span: TokenSpan::new(start_token, end_token),
|
||||
});
|
||||
}
|
||||
|
|
@ -142,6 +149,7 @@ pub fn parse_protected_type_declaration(
|
|||
fn parse_physical_type_definition(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
range: Range,
|
||||
units_token: TokenId,
|
||||
) -> ParseResult<(TypeDefinition, Option<Ident>)> {
|
||||
let primary_unit = WithDecl::new(ctx.stream.expect_ident()?);
|
||||
expect_semicolon(ctx);
|
||||
|
|
@ -164,12 +172,13 @@ fn parse_physical_type_definition(
|
|||
value_token_id,
|
||||
AbstractLiteral => {
|
||||
let value = value_token.to_abstract_literal(value_token_id)?.item;
|
||||
let unit_token = ctx.stream.get_current_token_id();
|
||||
let unit = ctx.stream.expect_ident()?;
|
||||
PhysicalLiteral {value, unit: unit.into_ref()}
|
||||
WithTokenSpan::new(PhysicalLiteral {value, unit: unit.into_ref()}, TokenSpan::new(value_token_id, unit_token))
|
||||
},
|
||||
Identifier => {
|
||||
let unit = value_token.to_identifier_value(value_token_id)?;
|
||||
PhysicalLiteral {value: AbstractLiteral::Integer(1), unit: unit.into_ref()}
|
||||
WithTokenSpan::new(PhysicalLiteral {value: AbstractLiteral::Integer(1), unit: unit.into_ref()}, TokenSpan::new(value_token_id, value_token_id))
|
||||
}
|
||||
)
|
||||
};
|
||||
|
|
@ -187,6 +196,7 @@ fn parse_physical_type_definition(
|
|||
Ok((
|
||||
TypeDefinition::Physical(PhysicalTypeDeclaration {
|
||||
range,
|
||||
units_token,
|
||||
primary_unit,
|
||||
secondary_units,
|
||||
}),
|
||||
|
|
@ -229,13 +239,13 @@ pub fn parse_type_declaration(ctx: &mut ParsingContext<'_>) -> ParseResult<TypeD
|
|||
Range => {
|
||||
let constraint = parse_range(ctx)?.item;
|
||||
expect_token!(
|
||||
ctx.stream, token,
|
||||
ctx.stream, token, token_id,
|
||||
SemiColon => {
|
||||
ctx.stream.back(); // The ';' is consumed at the end of the function
|
||||
TypeDefinition::Numeric(constraint)
|
||||
},
|
||||
Units => {
|
||||
let (def, end_ident) = parse_physical_type_definition(ctx, constraint)?;
|
||||
let (def, end_ident) = parse_physical_type_definition(ctx, constraint, token_id)?;
|
||||
end_ident_pos = check_end_identifier_mismatch(ctx, &ident.tree, end_ident);
|
||||
def
|
||||
}
|
||||
|
|
@ -400,6 +410,7 @@ mod tests {
|
|||
vec![ArrayIndex::IndexSubtypeDefintion(
|
||||
code.s1("natural").type_mark(),
|
||||
)],
|
||||
code.s1("of").token(),
|
||||
code.s1("boolean").subtype_indication(),
|
||||
),
|
||||
end_ident_pos: None,
|
||||
|
|
@ -419,10 +430,11 @@ mod tests {
|
|||
span: code.token_span(),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
def: TypeDefinition::Array(
|
||||
vec![ArrayIndex::Discrete(DiscreteRange::Discrete(
|
||||
code.s1("natural").type_mark(),
|
||||
None,
|
||||
vec![ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Discrete(code.s1("natural").type_mark(), None),
|
||||
code.s1("natural").token_span(),
|
||||
))],
|
||||
code.s1("of").token(),
|
||||
code.s1("boolean").subtype_indication(),
|
||||
),
|
||||
end_ident_pos: None,
|
||||
|
|
@ -442,10 +454,11 @@ mod tests {
|
|||
span: code.token_span(),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
def: TypeDefinition::Array(
|
||||
vec![ArrayIndex::Discrete(DiscreteRange::Discrete(
|
||||
code.s1("lib.pkg.foo").type_mark(),
|
||||
None,
|
||||
vec![ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Discrete(code.s1("lib.pkg.foo").type_mark(), None),
|
||||
code.s1("lib.pkg.foo").token_span(),
|
||||
))],
|
||||
code.s1("of").token(),
|
||||
code.s1("boolean").subtype_indication(),
|
||||
),
|
||||
end_ident_pos: None,
|
||||
|
|
@ -465,9 +478,11 @@ mod tests {
|
|||
span: code.token_span(),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
def: TypeDefinition::Array(
|
||||
vec![ArrayIndex::Discrete(DiscreteRange::Range(
|
||||
code.s1("arr_t'range").range(),
|
||||
vec![ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Range(code.s1("arr_t'range").range()),
|
||||
code.s1("arr_t'range").token_span(),
|
||||
))],
|
||||
code.s1("of").token(),
|
||||
code.s1("boolean").subtype_indication(),
|
||||
),
|
||||
end_ident_pos: None,
|
||||
|
|
@ -483,12 +498,19 @@ mod tests {
|
|||
fn parse_array_type_definition_with_constraint() {
|
||||
let code = Code::new("type foo is array (2-1 downto 0) of boolean;");
|
||||
|
||||
let index = ArrayIndex::Discrete(DiscreteRange::Range(code.s1("2-1 downto 0").range()));
|
||||
let index = ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Range(code.s1("2-1 downto 0").range()),
|
||||
code.s1("2-1 downto 0").token_span(),
|
||||
));
|
||||
|
||||
let type_decl = TypeDeclaration {
|
||||
span: code.token_span(),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
def: TypeDefinition::Array(vec![index], code.s1("boolean").subtype_indication()),
|
||||
def: TypeDefinition::Array(
|
||||
vec![index],
|
||||
code.s1("of").token(),
|
||||
code.s1("boolean").subtype_indication(),
|
||||
),
|
||||
end_ident_pos: None,
|
||||
};
|
||||
|
||||
|
|
@ -502,7 +524,10 @@ mod tests {
|
|||
fn parse_array_type_definition_mixed() {
|
||||
let code = Code::new("type foo is array (2-1 downto 0, integer range <>) of boolean;");
|
||||
|
||||
let index0 = ArrayIndex::Discrete(DiscreteRange::Range(code.s1("2-1 downto 0").range()));
|
||||
let index0 = ArrayIndex::Discrete(WithTokenSpan::new(
|
||||
DiscreteRange::Range(code.s1("2-1 downto 0").range()),
|
||||
code.s1("2-1 downto 0").token_span(),
|
||||
));
|
||||
|
||||
let index1 = ArrayIndex::IndexSubtypeDefintion(code.s1("integer").type_mark());
|
||||
|
||||
|
|
@ -511,6 +536,7 @@ mod tests {
|
|||
ident: code.s1("foo").decl_ident(),
|
||||
def: TypeDefinition::Array(
|
||||
vec![index0, index1],
|
||||
code.s1("of").token(),
|
||||
code.s1("boolean").subtype_indication(),
|
||||
),
|
||||
end_ident_pos: None,
|
||||
|
|
@ -534,6 +560,7 @@ end record;",
|
|||
let elem_decl = ElementDeclaration {
|
||||
idents: vec![code.s1("element").decl_ident()],
|
||||
subtype: code.s1("boolean").subtype_indication(),
|
||||
colon_token: code.s1(":").token(),
|
||||
span: code.s1("element : boolean;").token_span(),
|
||||
};
|
||||
|
||||
|
|
@ -565,12 +592,14 @@ end foo;",
|
|||
code.s1("element").decl_ident(),
|
||||
code.s1("field").decl_ident(),
|
||||
],
|
||||
colon_token: code.s1(":").token(),
|
||||
subtype: code.s1("boolean").subtype_indication(),
|
||||
span: code.s1("element, field : boolean;").token_span(),
|
||||
};
|
||||
|
||||
let elem_decl1 = ElementDeclaration {
|
||||
idents: vec![code.s1("other_element").decl_ident()],
|
||||
colon_token: code.s(":", 2).token(),
|
||||
subtype: code.s1("std_logic_vector(0 to 1)").subtype_indication(),
|
||||
span: code
|
||||
.s1("other_element : std_logic_vector(0 to 1);")
|
||||
|
|
@ -774,6 +803,7 @@ end units phys;
|
|||
ident: code.s1("phys").decl_ident(),
|
||||
def: TypeDefinition::Physical(PhysicalTypeDeclaration {
|
||||
range: code.s1("0 to 15").range(),
|
||||
units_token: code.s1("units").token(),
|
||||
primary_unit: code.s1("primary_unit").decl_ident(),
|
||||
secondary_units: vec![]
|
||||
}),
|
||||
|
|
@ -800,13 +830,17 @@ end units;
|
|||
ident: code.s1("phys").decl_ident(),
|
||||
def: TypeDefinition::Physical(PhysicalTypeDeclaration {
|
||||
range: code.s1("0 to 15").range(),
|
||||
units_token: code.s1("units").token(),
|
||||
primary_unit: code.s1("primary_unit").decl_ident(),
|
||||
secondary_units: vec![(
|
||||
code.s1("secondary_unit").decl_ident(),
|
||||
PhysicalLiteral {
|
||||
value: AbstractLiteral::Integer(5),
|
||||
unit: code.s("primary_unit", 2).ident().into_ref()
|
||||
}
|
||||
WithTokenSpan::new(
|
||||
PhysicalLiteral {
|
||||
value: AbstractLiteral::Integer(5),
|
||||
unit: code.s("primary_unit", 2).ident().into_ref()
|
||||
},
|
||||
code.s1("5 primary_unit").token_span()
|
||||
)
|
||||
),]
|
||||
}),
|
||||
end_ident_pos: None,
|
||||
|
|
@ -832,13 +866,17 @@ end units;
|
|||
ident: code.s1("phys").decl_ident(),
|
||||
def: TypeDefinition::Physical(PhysicalTypeDeclaration {
|
||||
range: code.s1("0 to 15").range(),
|
||||
units_token: code.s1("units").token(),
|
||||
primary_unit: code.s1("primary_unit").decl_ident(),
|
||||
secondary_units: vec![(
|
||||
code.s1("secondary_unit").decl_ident(),
|
||||
PhysicalLiteral {
|
||||
value: AbstractLiteral::Integer(1),
|
||||
unit: code.s("primary_unit", 2).ident().into_ref()
|
||||
}
|
||||
WithTokenSpan::new(
|
||||
PhysicalLiteral {
|
||||
value: AbstractLiteral::Integer(1),
|
||||
unit: code.s("primary_unit", 2).ident().into_ref()
|
||||
},
|
||||
code.s("primary_unit", 2).token_span()
|
||||
)
|
||||
),]
|
||||
}),
|
||||
end_ident_pos: None,
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ use crate::ast::token_range::WithTokenSpan;
|
|||
use crate::ast::{ElementMode, ModeViewDeclaration, ModeViewElement, WithDecl};
|
||||
use crate::syntax::common::ParseResult;
|
||||
use crate::syntax::interface_declaration::parse_optional_mode;
|
||||
use crate::syntax::names::parse_name;
|
||||
use crate::syntax::names::{parse_identifier_list, parse_name};
|
||||
use crate::syntax::parser::ParsingContext;
|
||||
use crate::syntax::recover::expect_semicolon_or_last;
|
||||
use crate::syntax::separated_list::parse_ident_list;
|
||||
use crate::syntax::subtype_indication::parse_subtype_indication;
|
||||
use crate::syntax::Kind::*;
|
||||
use itertools::Itertools;
|
||||
use vhdl_lang::syntax::common::check_end_identifier_mismatch;
|
||||
use vhdl_lang::TokenSpan;
|
||||
|
||||
|
|
@ -28,12 +28,12 @@ pub(crate) fn parse_mode_view_declaration(
|
|||
let ident = WithDecl::new(ctx.stream.expect_ident()?);
|
||||
ctx.stream.expect_kind(Of)?;
|
||||
let typ = parse_subtype_indication(ctx)?;
|
||||
ctx.stream.expect_kind(Is)?;
|
||||
let is_token = ctx.stream.expect_kind(Is)?;
|
||||
let mut elements = Vec::new();
|
||||
while ctx.stream.peek_kind() != Some(End) {
|
||||
elements.push(parse_mode_view_element_definition(ctx)?);
|
||||
}
|
||||
ctx.stream.expect_kind(End)?;
|
||||
let end_token = ctx.stream.expect_kind(End)?;
|
||||
ctx.stream.expect_kind(View)?;
|
||||
let end_ident_pos =
|
||||
check_end_identifier_mismatch(ctx, &ident.tree, ctx.stream.pop_optional_ident());
|
||||
|
|
@ -42,7 +42,9 @@ pub(crate) fn parse_mode_view_declaration(
|
|||
ModeViewDeclaration {
|
||||
ident,
|
||||
typ,
|
||||
is_token,
|
||||
elements,
|
||||
end_token,
|
||||
end_ident_pos,
|
||||
},
|
||||
TokenSpan::new(start_tok, end_tok),
|
||||
|
|
@ -54,14 +56,18 @@ pub(crate) fn parse_mode_view_element_definition(
|
|||
ctx: &mut ParsingContext<'_>,
|
||||
) -> ParseResult<ModeViewElement> {
|
||||
let start = ctx.stream.get_current_token_id();
|
||||
let element_list = parse_ident_list(ctx)?;
|
||||
ctx.stream.expect_kind(Colon)?;
|
||||
let names = parse_identifier_list(ctx)?
|
||||
.into_iter()
|
||||
.map(WithDecl::new)
|
||||
.collect_vec();
|
||||
let colon_token = ctx.stream.expect_kind(Colon)?;
|
||||
let mode = parse_element_mode_indication(ctx)?;
|
||||
let end_token = expect_semicolon_or_last(ctx);
|
||||
Ok(ModeViewElement {
|
||||
span: TokenSpan::new(start, end_token),
|
||||
mode,
|
||||
names: element_list,
|
||||
colon_token,
|
||||
names,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +178,8 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.parse_ok_no_diagnostics(parse_mode_view_element_definition),
|
||||
ModeViewElement {
|
||||
names: code.s1("foo").ident_list(),
|
||||
names: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
mode: code.s1("in").element_mode(),
|
||||
span: code.token_span(),
|
||||
}
|
||||
|
|
@ -182,7 +189,12 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.parse_ok_no_diagnostics(parse_mode_view_element_definition),
|
||||
ModeViewElement {
|
||||
names: code.s1("foo, bar, baz").ident_list(),
|
||||
names: vec![
|
||||
code.s1("foo").decl_ident(),
|
||||
code.s1("bar").decl_ident(),
|
||||
code.s1("baz").decl_ident()
|
||||
],
|
||||
colon_token: code.s1(":").token(),
|
||||
mode: code.s1("linkage").element_mode(),
|
||||
span: code.token_span(),
|
||||
}
|
||||
|
|
@ -192,7 +204,8 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.parse_ok_no_diagnostics(parse_mode_view_element_definition),
|
||||
ModeViewElement {
|
||||
names: code.s1("foo").ident_list(),
|
||||
names: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
mode: code.s1("view bar").element_mode(),
|
||||
span: code.token_span(),
|
||||
}
|
||||
|
|
@ -202,7 +215,8 @@ mod tests {
|
|||
assert_eq!(
|
||||
code.parse_ok_no_diagnostics(parse_mode_view_element_definition),
|
||||
ModeViewElement {
|
||||
names: code.s1("foo").ident_list(),
|
||||
names: vec![code.s1("foo").decl_ident()],
|
||||
colon_token: code.s1(":").token(),
|
||||
mode: code.s1("view (bar_array)").element_mode(),
|
||||
span: code.token_span(),
|
||||
}
|
||||
|
|
@ -224,7 +238,9 @@ end view;
|
|||
ModeViewDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
typ: code.s1("bar").subtype_indication(),
|
||||
is_token: code.s1("is").token(),
|
||||
elements: Vec::new(),
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
},
|
||||
code.token_span()
|
||||
|
|
@ -244,7 +260,9 @@ end view foo;
|
|||
ModeViewDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
typ: code.s1("bar").subtype_indication(),
|
||||
is_token: code.s1("is").token(),
|
||||
elements: Vec::new(),
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: Some(code.s("foo", 2).token()),
|
||||
},
|
||||
code.token_span()
|
||||
|
|
@ -265,7 +283,9 @@ end view baz;
|
|||
ModeViewDeclaration {
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
typ: code.s1("bar").subtype_indication(),
|
||||
is_token: code.s1("is").token(),
|
||||
elements: Vec::new(),
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None
|
||||
},
|
||||
code.token_span()
|
||||
|
|
@ -296,12 +316,14 @@ end view;
|
|||
code.parse_ok_no_diagnostics(parse_mode_view_declaration),
|
||||
WithTokenSpan::new(
|
||||
ModeViewDeclaration {
|
||||
typ: code.s1("bar").subtype_indication(),
|
||||
ident: code.s1("foo").decl_ident(),
|
||||
typ: code.s1("bar").subtype_indication(),
|
||||
is_token: code.s1("is").token(),
|
||||
elements: vec![
|
||||
code.s1("baz: in;").mode_view_element(),
|
||||
code.s1("foo: view some_view;").mode_view_element(),
|
||||
],
|
||||
end_token: code.s1("end").token(),
|
||||
end_ident_pos: None,
|
||||
},
|
||||
code.token_span()
|
||||
|
|
|
|||
|
|
@ -4,30 +4,44 @@
|
|||
//
|
||||
// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::ast::token_range::WithTokenSpan;
|
||||
use crate::ast::{DelayMechanism, Waveform, WaveformElement};
|
||||
use crate::syntax::parser::ParsingContext;
|
||||
use vhdl_lang::TokenSpan;
|
||||
|
||||
use super::common::{parse_optional, ParseResult};
|
||||
use super::expression::parse_expression;
|
||||
use super::tokens::Kind::*;
|
||||
|
||||
/// LRM 10.5 Signal assignment statement
|
||||
pub fn parse_delay_mechanism(ctx: &mut ParsingContext<'_>) -> ParseResult<Option<DelayMechanism>> {
|
||||
pub fn parse_delay_mechanism(
|
||||
ctx: &mut ParsingContext<'_>,
|
||||
) -> ParseResult<Option<WithTokenSpan<DelayMechanism>>> {
|
||||
let token = ctx.stream.peek_expect()?;
|
||||
let start_token = ctx.stream.get_current_token_id();
|
||||
match token.kind {
|
||||
Transport => {
|
||||
ctx.stream.skip();
|
||||
Ok(Some(DelayMechanism::Transport))
|
||||
let span = TokenSpan::new(start_token, start_token);
|
||||
Ok(Some(WithTokenSpan::new(DelayMechanism::Transport, span)))
|
||||
}
|
||||
Inertial => {
|
||||
ctx.stream.skip();
|
||||
Ok(Some(DelayMechanism::Inertial { reject: None }))
|
||||
let span = TokenSpan::new(start_token, start_token);
|
||||
Ok(Some(WithTokenSpan::new(
|
||||
DelayMechanism::Inertial { reject: None },
|
||||
span,
|
||||
)))
|
||||
}
|
||||
Reject => {
|
||||
ctx.stream.skip();
|
||||
let reject = Some(parse_expression(ctx)?);
|
||||
ctx.stream.expect_kind(Inertial)?;
|
||||
Ok(Some(DelayMechanism::Inertial { reject }))
|
||||
let end_token = ctx.stream.expect_kind(Inertial)?;
|
||||
let span = TokenSpan::new(start_token, end_token);
|
||||
Ok(Some(WithTokenSpan::new(
|
||||
DelayMechanism::Inertial { reject },
|
||||
span,
|
||||
)))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
|
|
@ -35,8 +49,8 @@ pub fn parse_delay_mechanism(ctx: &mut ParsingContext<'_>) -> ParseResult<Option
|
|||
|
||||
/// LRM 10.5 Signal assignment statement
|
||||
pub fn parse_waveform(ctx: &mut ParsingContext<'_>) -> ParseResult<Waveform> {
|
||||
if ctx.stream.skip_if_kind(Unaffected) {
|
||||
return Ok(Waveform::Unaffected);
|
||||
if let Some(token) = ctx.stream.pop_if_kind(Unaffected) {
|
||||
return Ok(Waveform::Unaffected(token));
|
||||
}
|
||||
|
||||
let mut elems = Vec::new();
|
||||
|
|
@ -64,7 +78,10 @@ mod tests {
|
|||
let code = Code::new("transport");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_delay_mechanism),
|
||||
Some(DelayMechanism::Transport)
|
||||
Some(WithTokenSpan::new(
|
||||
DelayMechanism::Transport,
|
||||
code.token_span()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +90,10 @@ mod tests {
|
|||
let code = Code::new("inertial");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_delay_mechanism),
|
||||
Some(DelayMechanism::Inertial { reject: None })
|
||||
Some(WithTokenSpan::new(
|
||||
DelayMechanism::Inertial { reject: None },
|
||||
code.token_span()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -82,9 +102,12 @@ mod tests {
|
|||
let code = Code::new("reject 2 ns inertial");
|
||||
assert_eq!(
|
||||
code.with_stream(parse_delay_mechanism),
|
||||
Some(DelayMechanism::Inertial {
|
||||
reject: Some(code.s1("2 ns").expr())
|
||||
})
|
||||
Some(WithTokenSpan::new(
|
||||
DelayMechanism::Inertial {
|
||||
reject: Some(code.s1("2 ns").expr())
|
||||
},
|
||||
code.token_span()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -133,6 +156,9 @@ mod tests {
|
|||
#[test]
|
||||
fn test_unaffected_waveform() {
|
||||
let code = Code::new("unaffected");
|
||||
assert_eq!(code.with_stream(parse_waveform), Waveform::Unaffected);
|
||||
assert_eq!(
|
||||
code.with_stream(parse_waveform),
|
||||
Waveform::Unaffected(code.token())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
81
vhdl_lang/tests/format_example_project.rs
Normal file
81
vhdl_lang/tests/format_example_project.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::iter::zip;
|
||||
use std::path::{Path, PathBuf};
|
||||
use vhdl_lang::{Diagnostic, SeverityMap, Source, VHDLFormatter, VHDLParser, VHDLStandard};
|
||||
|
||||
// excluded file contains PSL statements
|
||||
const EXCLUDED_FILES: [&str; 1] = ["vunit/examples/vhdl/array_axis_vcs/src/fifo.vhd"];
|
||||
|
||||
fn format_file(path: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let severity_map = SeverityMap::default();
|
||||
let parser = VHDLParser::new(VHDLStandard::default());
|
||||
let mut diagnostics = Vec::new();
|
||||
let (_, design_file) = parser.parse_design_file(path, &mut diagnostics)?;
|
||||
if !diagnostics.is_empty() {
|
||||
for diagnostic in diagnostics {
|
||||
println!("{}", diagnostic.show(&severity_map).unwrap())
|
||||
}
|
||||
panic!("Found diagnostics with severity error in the example project");
|
||||
}
|
||||
|
||||
let result = VHDLFormatter::format_design_file(&design_file);
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
let new_file = parser.parse_design_source(&Source::inline(path, &result), &mut diagnostics);
|
||||
if !diagnostics.is_empty() {
|
||||
for diagnostic in diagnostics {
|
||||
println!("{}", diagnostic.show(&severity_map).unwrap())
|
||||
}
|
||||
panic!("Formatting failed! File was OK before, but is not after");
|
||||
}
|
||||
for ((tokens_a, _), (tokens_b, _)) in zip(new_file.design_units, design_file.design_units) {
|
||||
for (a, b) in zip(tokens_a, tokens_b) {
|
||||
if !a.equal_format(&b) {
|
||||
println!("Token mismatch");
|
||||
println!("New Token={a:#?}");
|
||||
let contents = a.pos.source.contents();
|
||||
let a_line = contents.get_line(a.pos.range.start.line as usize).unwrap();
|
||||
println!(" {a_line}");
|
||||
println!("Old Token={b:#?}");
|
||||
let b_line = result.lines().nth(b.pos.range.start.line as usize).unwrap();
|
||||
println!(" {b_line}");
|
||||
panic!("Token Mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_dir(path: &Path) -> Result<(), Box<dyn Error>> {
|
||||
for entry in fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let file_type = entry.file_type()?;
|
||||
if file_type.is_dir() {
|
||||
format_dir(&entry.path())?
|
||||
} else if let Some(extension) = entry.path().extension() {
|
||||
if (extension == "vhd" || extension == "vhdl") && !is_file_excluded(&entry.path()) {
|
||||
format_file(&entry.path())?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_file_excluded(path: &Path) -> bool {
|
||||
for file in EXCLUDED_FILES {
|
||||
if path.ends_with(file) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Checks that all files in the example project are correctly formatted
|
||||
// while retaining their token stream.
|
||||
#[test]
|
||||
fn formats_all_vhdl_files_without_producing_different_code() -> Result<(), Box<dyn Error>> {
|
||||
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
path.push("../example_project");
|
||||
format_dir(&path)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue