use super::common::error_on_end_identifier_mismatch;
use super::common::ParseResult;
use super::declarative_part::{is_declarative_part, parse_declarative_part};
use super::expression::parse_aggregate_leftpar_known;
use super::expression::{parse_choices, parse_expression};
use super::interface_declaration::{parse_generic_interface_list, parse_port_interface_list};
use super::names::{
expression_to_ident, into_selected_name, parse_association_list, parse_name_initial_token,
parse_selected_name,
};
use super::range::parse_discrete_range;
use super::sequential_statement::{
parse_assert_statement_known_keyword, parse_labeled_sequential_statements, parse_selection,
parse_signal_assignment_right_hand, parse_target,
};
use super::tokens::{Kind::*, Token, TokenStream};
use super::waveform::{parse_delay_mechanism, parse_waveform};
use crate::ast::*;
use crate::data::*;
pub fn parse_block_statement(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<BlockStatement> {
let token = stream.peek_expect()?;
let guard_condition = {
match token.kind {
LeftPar => {
stream.move_after(&token);
let expr = parse_expression(stream)?;
stream.pop_if_kind(RightPar)?;
Some(expr)
}
_ => None,
}
};
stream.pop_if_kind(Is)?;
let header = parse_block_header(stream, diagnostics)?;
let decl = parse_declarative_part(stream, diagnostics, true)?;
let statements = parse_labeled_concurrent_statements(stream, diagnostics)?;
stream.expect_kind(Block)?;
stream.pop_if_kind(Identifier)?;
stream.expect_kind(SemiColon)?;
Ok(BlockStatement {
guard_condition,
header,
decl,
statements,
})
}
fn parse_block_header(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<BlockHeader> {
let mut generic_clause = None;
let mut generic_map = None;
let mut port_clause = None;
let mut port_map = None;
loop {
let token = stream.peek_expect()?;
match token.kind {
Generic => {
stream.move_after(&token);
if let Some(map_token) = stream.pop_if_kind(Map)? {
if port_clause.is_some() || port_map.is_some() {
diagnostics.push(Diagnostic::error(
map_token,
"Generic map must come before port clause and port map",
));
} else if generic_clause.is_none() {
diagnostics.push(Diagnostic::error(
map_token,
"Generic map declared without preceeding generic clause",
));
} else if generic_map.is_some() {
diagnostics.push(Diagnostic::error(map_token, "Duplicate generic map"));
}
let parsed_generic_map = Some(parse_association_list(stream)?);
stream.expect_kind(SemiColon)?;
if generic_map.is_none() {
generic_map = parsed_generic_map;
}
} else {
if generic_map.is_some() {
diagnostics.push(Diagnostic::error(
token,
"Generic clause must come before generic map",
));
} else if generic_clause.is_some() {
diagnostics.push(Diagnostic::error(token, "Duplicate generic clause"));
}
let parsed_generic_list = parse_generic_interface_list(stream, diagnostics)?;
stream.expect_kind(SemiColon)?;
if generic_clause.is_none() {
generic_clause = Some(parsed_generic_list);
}
}
}
Port => {
stream.move_after(&token);
if let Some(map_token) = stream.pop_if_kind(Map)? {
if port_clause.is_none() {
diagnostics.push(Diagnostic::error(
map_token,
"Port map declared without preceeding port clause",
));
} else if port_map.is_some() {
diagnostics.push(Diagnostic::error(map_token, "Duplicate port map"));
}
let parsed_port_map = Some(parse_association_list(stream)?);
stream.expect_kind(SemiColon)?;
if port_map.is_none() {
port_map = parsed_port_map;
}
} else {
if port_map.is_some() {
diagnostics.push(Diagnostic::error(
token,
"Port clause declared after port map",
));
} else if port_clause.is_some() {
diagnostics.push(Diagnostic::error(token, "Duplicate port clause"));
}
let parsed_port_list = parse_port_interface_list(stream, diagnostics)?;
stream.expect_kind(SemiColon)?;
if port_clause.is_none() {
port_clause = Some(parsed_port_list);
}
}
}
_ => break,
}
}
Ok(BlockHeader {
generic_clause,
generic_map,
port_clause,
port_map,
})
}
pub fn parse_process_statement(
stream: &mut TokenStream,
postponed: bool,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<ProcessStatement> {
let token = stream.peek_expect()?;
let sensitivity_list = {
match token.kind {
LeftPar => {
stream.move_after(&token);
let token = stream.expect()?;
if token.kind == All {
stream.expect_kind(RightPar)?;
Some(SensitivityList::All)
} else {
let mut names = Vec::with_capacity(1);
let mut token = token;
loop {
match token.kind {
RightPar => {
if names.is_empty() {
diagnostics.push(
Diagnostic::error(token, "Processes with sensitivity lists must contain at least one element.")
);
}
break Some(SensitivityList::Names(names));
}
Comma => {}
_ => {
names.push(parse_name_initial_token(stream, token)?);
}
};
token = stream.expect()?;
}
}
}
_ => None,
}
};
stream.pop_if_kind(Is)?;
let decl = parse_declarative_part(stream, diagnostics, true)?;
let (statements, end_token) = parse_labeled_sequential_statements(stream, diagnostics)?;
try_token_kind!(end_token, End => {});
if let Some(token) = stream.pop_if_kind(Postponed)? {
if !postponed {
diagnostics.push(Diagnostic::error(
token,
"'postponed' at the end of non-postponed process.",
));
}
}
stream.expect_kind(Process)?;
stream.pop_if_kind(Identifier)?;
stream.expect_kind(SemiColon)?;
Ok(ProcessStatement {
postponed,
sensitivity_list,
decl,
statements,
})
}
fn to_procedure_call(
target: WithPos<Target>,
postponed: bool,
) -> ParseResult<ConcurrentProcedureCall> {
match target.item {
Target::Name(Name::CallOrIndexed(call)) => Ok(ConcurrentProcedureCall {
postponed,
call: WithPos::new(*call, target.pos),
}),
Target::Name(name) => Ok(ConcurrentProcedureCall {
postponed,
call: WithPos::from(
CallOrIndexed {
name: WithPos::from(name, target.pos.clone()),
parameters: vec![],
},
target.pos,
),
}),
Target::Aggregate(..) => Err(Diagnostic::error(
target,
"Expected procedure call, got aggregate",
)),
}
}
fn parse_assignment_known_target(
stream: &mut TokenStream,
target: WithPos<Target>,
) -> ParseResult<ConcurrentStatement> {
let postponed = false;
let guarded = false;
let delay_mechanism = parse_delay_mechanism(stream)?;
Ok(ConcurrentStatement::Assignment(
ConcurrentSignalAssignment {
postponed,
guarded,
target,
delay_mechanism,
rhs: parse_signal_assignment_right_hand(stream)?,
},
))
}
fn parse_assignment_or_procedure_call(
stream: &mut TokenStream,
token: &Token,
target: WithPos<Target>,
) -> ParseResult<ConcurrentStatement> {
match_token_kind!(
token,
LTE => {
parse_assignment_known_target(stream, target)
},
SemiColon => {
Ok(ConcurrentStatement::ProcedureCall(to_procedure_call(target, false)?))
})
}
fn parse_selected_signal_assignment(
stream: &mut TokenStream,
postponed: bool,
) -> ParseResult<ConcurrentSignalAssignment> {
let expression = parse_expression(stream)?;
stream.expect_kind(Select)?;
let target = parse_target(stream)?;
stream.expect_kind(LTE)?;
let guarded = false;
let delay_mechanism = parse_delay_mechanism(stream)?;
let rhs = AssignmentRightHand::Selected(parse_selection(stream, expression, parse_waveform)?);
Ok(ConcurrentSignalAssignment {
postponed,
guarded,
target,
delay_mechanism,
rhs,
})
}
pub fn parse_concurrent_assert_statement(
stream: &mut TokenStream,
postponed: bool,
) -> ParseResult<ConcurrentAssertStatement> {
Ok(ConcurrentAssertStatement {
postponed,
statement: parse_assert_statement_known_keyword(stream)?,
})
}
#[allow(clippy::type_complexity)]
pub fn parse_generic_and_port_map(
stream: &mut TokenStream,
) -> ParseResult<(
Option<Vec<AssociationElement>>,
Option<Vec<AssociationElement>>,
)> {
let generic_map = {
if stream.skip_if_kind(Generic)? {
stream.expect_kind(Map)?;
Some(parse_association_list(stream)?)
} else {
None
}
};
let port_map = {
if stream.skip_if_kind(Port)? {
stream.expect_kind(Map)?;
Some(parse_association_list(stream)?)
} else {
None
}
};
Ok((generic_map, port_map))
}
pub fn parse_instantiation_statement(
stream: &mut TokenStream,
unit: InstantiatedUnit,
) -> ParseResult<InstantiationStatement> {
let (generic_map, port_map) = parse_generic_and_port_map(stream)?;
let inst = InstantiationStatement {
unit,
generic_map: generic_map.unwrap_or_default(),
port_map: port_map.unwrap_or_default(),
};
stream.expect_kind(SemiColon)?;
Ok(inst)
}
fn parse_optional_declarative_part(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<Option<Vec<Declaration>>> {
if is_declarative_part(stream, true)? {
Ok(Some(parse_declarative_part(stream, diagnostics, true)?))
} else {
Ok(None)
}
}
fn parse_generate_body_end_token(
stream: &mut TokenStream,
alternative_label: Option<WithDecl<Ident>>,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<(GenerateBody, Token)> {
let decl = parse_optional_declarative_part(stream, diagnostics)?;
let (statements, mut end_token) =
parse_labeled_concurrent_statements_end_token(stream, diagnostics)?;
if end_token.kind == End {
let token = stream.peek_expect()?;
try_token_kind!(
token,
Generate => {
},
SemiColon => {
stream.move_after(&token);
end_token = stream.expect()?;
},
Identifier => {
stream.move_after(&token);
let end_ident = token.expect_ident()?;
if let Some(ref ident) = alternative_label {
diagnostics.push_some(error_on_end_identifier_mismatch(&ident.tree, &Some(end_ident)));
};
stream.expect_kind(SemiColon)?;
end_token = stream.expect()?;
});
}
let body = GenerateBody {
alternative_label,
decl,
statements,
};
Ok((body, end_token))
}
fn parse_generate_body(
stream: &mut TokenStream,
alternative_label: Option<WithDecl<Ident>>,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<GenerateBody> {
let (body, end_token) = parse_generate_body_end_token(stream, alternative_label, diagnostics)?;
end_token.expect_kind(End)?;
Ok(body)
}
fn parse_for_generate_statement(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<ForGenerateStatement> {
let index_name = WithDecl::new(stream.expect_ident()?);
stream.expect_kind(In)?;
let discrete_range = parse_discrete_range(stream)?;
stream.expect_kind(Generate)?;
let body = parse_generate_body(stream, None, diagnostics)?;
stream.expect_kind(Generate)?;
stream.pop_if_kind(Identifier)?;
stream.expect_kind(SemiColon)?;
Ok(ForGenerateStatement {
index_name,
discrete_range,
body,
})
}
fn parse_if_generate_statement(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<IfGenerateStatement> {
let mut conditionals = Vec::new();
let else_branch;
loop {
let mut condition = parse_expression(stream)?;
let mut alternative_label = None;
if stream.skip_if_kind(Colon)? {
alternative_label = Some(WithDecl::new(expression_to_ident(condition)?));
condition = parse_expression(stream)?;
}
stream.expect_kind(Generate)?;
let (body, end_token) =
parse_generate_body_end_token(stream, alternative_label, diagnostics)?;
let conditional = Conditional {
condition,
item: body,
};
conditionals.push(conditional);
try_token_kind!(
end_token,
End => {
else_branch = None;
break;
},
Elsif => {
continue;
},
Else => {
let token = stream.expect()?;
let alternative_label = try_token_kind!(
token,
Generate => {
None
},
Identifier => {
stream.expect_kind(Colon)?;
stream.expect_kind(Generate)?;
Some(WithDecl::new(token.expect_ident()?))
}
);
let body = parse_generate_body(stream, alternative_label, diagnostics)?;
else_branch = Some(body);
break;
}
);
}
stream.expect_kind(Generate)?;
stream.pop_if_kind(Identifier)?;
stream.expect_kind(SemiColon)?;
Ok(IfGenerateStatement {
conditionals,
else_item: else_branch,
})
}
fn parse_case_generate_statement(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<CaseGenerateStatement> {
let expression = parse_expression(stream)?;
stream.expect_kind(Generate)?;
stream.expect_kind(When)?;
let mut alternatives = Vec::with_capacity(2);
loop {
let alternative_label = {
if stream.next_kinds_are(&[Identifier, Colon])? {
let ident = stream.expect_ident()?;
stream.expect_kind(Colon)?;
Some(WithDecl::new(ident))
} else {
None
}
};
let choices = parse_choices(stream)?;
stream.expect_kind(RightArrow)?;
let (body, end_token) =
parse_generate_body_end_token(stream, alternative_label, diagnostics)?;
alternatives.push(Alternative {
choices,
item: body,
});
try_token_kind!(
end_token,
End => break,
When => continue
);
}
stream.expect_kind(Generate)?;
stream.pop_if_kind(Identifier)?;
stream.expect_kind(SemiColon)?;
Ok(CaseGenerateStatement {
expression,
alternatives,
})
}
pub fn parse_concurrent_statement(
stream: &mut TokenStream,
token: Token,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<ConcurrentStatement> {
let statement = {
try_token_kind!(
token,
Block => {
ConcurrentStatement::Block(parse_block_statement(stream, diagnostics)?)
},
Process => {
ConcurrentStatement::Process(parse_process_statement(stream, false, diagnostics)?)
},
Component => {
let unit = InstantiatedUnit::Component(parse_selected_name(stream)?);
ConcurrentStatement::Instance(parse_instantiation_statement(stream, unit)?)
},
Configuration => {
let unit = InstantiatedUnit::Configuration(parse_selected_name(stream)?);
ConcurrentStatement::Instance(parse_instantiation_statement(stream, unit)?)
},
Entity => {
let name = parse_selected_name(stream)?;
let arch = {
if stream.skip_if_kind(LeftPar)? {
let ident = stream.expect_ident()?;
stream.expect_kind(RightPar)?;
Some(ident)
} else {
None
}
};
let unit = InstantiatedUnit::Entity(name, arch);
ConcurrentStatement::Instance(parse_instantiation_statement(stream, unit)?)
},
For => ConcurrentStatement::ForGenerate(parse_for_generate_statement(stream, diagnostics)?),
If => ConcurrentStatement::IfGenerate(parse_if_generate_statement(stream, diagnostics)?),
Case => ConcurrentStatement::CaseGenerate(parse_case_generate_statement(stream, diagnostics)?),
Assert => ConcurrentStatement::Assert(parse_concurrent_assert_statement(stream, false)?),
Postponed => {
let token = stream.expect()?;
match token.kind {
Process => ConcurrentStatement::Process(parse_process_statement(stream, true, diagnostics)?),
Assert => ConcurrentStatement::Assert(parse_concurrent_assert_statement(stream, true)?),
With => ConcurrentStatement::Assignment(parse_selected_signal_assignment(stream, true)?),
_ => {
let target = parse_name_initial_token(stream, token)?.map_into(Target::Name);
stream.expect_kind(SemiColon)?;
ConcurrentStatement::ProcedureCall(to_procedure_call(target, true)?)
}
}
},
With => ConcurrentStatement::Assignment(parse_selected_signal_assignment(stream, false)?),
Identifier => {
let name = parse_name_initial_token(stream, token)?;
let token = stream.peek_expect()?;
match token.kind {
Generic|Port => {
let unit = InstantiatedUnit::Component(into_selected_name(name)?);
ConcurrentStatement::Instance(parse_instantiation_statement(stream, unit)?)
}
_ => {
stream.move_after(&token);
parse_assignment_or_procedure_call(stream, &token, name.map_into(Target::Name))?
}
}
},
LtLt => {
let name = parse_name_initial_token(stream, token)?;
stream.expect_kind(LTE)?;
parse_assignment_known_target(stream, name.map_into(Target::Name))?
},
LeftPar => {
let target = parse_aggregate_leftpar_known(stream)?.map_into(Target::Aggregate);
let token = stream.expect()?;
parse_assignment_or_procedure_call(stream, &token, target)?
}
)
};
Ok(statement)
}
pub fn parse_labeled_concurrent_statements_end_token(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<(Vec<LabeledConcurrentStatement>, Token)> {
let mut statements = Vec::new();
loop {
let token = stream.expect()?;
match token.kind {
End | Elsif | Else | When => {
break Ok((statements, token));
}
_ => {
statements.push(parse_labeled_concurrent_statement_initial_token(
stream,
token,
diagnostics,
)?);
}
}
}
}
pub fn parse_labeled_concurrent_statements(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<Vec<LabeledConcurrentStatement>> {
let (statement, _) = parse_labeled_concurrent_statements_end_token(stream, diagnostics)?;
Ok(statement)
}
pub fn parse_labeled_concurrent_statement_initial_token(
stream: &mut TokenStream,
token: Token,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<LabeledConcurrentStatement> {
if token.kind == Identifier {
let name = parse_name_initial_token(stream, token)?;
let token = stream.expect()?;
if token.kind == Colon {
let label = Some(WithDecl::new(to_simple_name(name)?));
let token = stream.expect()?;
let statement = parse_concurrent_statement(stream, token, diagnostics)?;
Ok(LabeledConcurrentStatement { label, statement })
} else {
let target = name.map_into(Target::Name);
let statement = parse_assignment_or_procedure_call(stream, &token, target)?;
Ok(LabeledConcurrentStatement {
label: None,
statement,
})
}
} else {
let statement = parse_concurrent_statement(stream, token, diagnostics)?;
Ok(LabeledConcurrentStatement {
label: None,
statement,
})
}
}
#[cfg(test)]
pub fn parse_labeled_concurrent_statement(
stream: &mut TokenStream,
diagnostics: &mut dyn DiagnosticHandler,
) -> ParseResult<LabeledConcurrentStatement> {
let token = stream.expect()?;
parse_labeled_concurrent_statement_initial_token(stream, token, diagnostics)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Alternative, AssertStatement, DelayMechanism, Selection};
use crate::syntax::test::Code;
use pretty_assertions::assert_eq;
#[test]
fn test_concurrent_procedure() {
let code = Code::new(
"\
foo(clk);
",
);
let call = ConcurrentProcedureCall {
postponed: false,
call: code.s1("foo(clk)").function_call(),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::ProcedureCall(call));
}
#[test]
fn test_postponed_concurrent_procedure() {
let code = Code::new(
"\
postponed foo(clk);
",
);
let call = ConcurrentProcedureCall {
postponed: true,
call: code.s1("foo(clk)").function_call(),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::ProcedureCall(call));
}
#[test]
fn test_labeled_concurrent_procedure() {
let code = Code::new(
"\
name: foo(clk);
",
);
let call = ConcurrentProcedureCall {
postponed: false,
call: code.s1("foo(clk)").function_call(),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("name").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::ProcedureCall(call));
}
#[test]
fn test_concurrent_procedure_no_args() {
let code = Code::new(
"\
foo;
",
);
let call = ConcurrentProcedureCall {
postponed: false,
call: code.s1("foo").function_call(),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::ProcedureCall(call));
}
#[test]
fn test_block() {
let code = Code::new(
"\
name : block
constant const : natural := 0;
begin
name2: foo(clk);
end block;
",
);
let call = ConcurrentProcedureCall {
postponed: false,
call: code.s1("foo(clk)").function_call(),
};
let block = BlockStatement {
guard_condition: None,
header: BlockHeader {
generic_clause: None,
generic_map: None,
port_clause: None,
port_map: None,
},
decl: code.s1("constant const : natural := 0;").declarative_part(),
statements: vec![LabeledConcurrentStatement {
label: Some(code.s1("name2").decl_ident()),
statement: ConcurrentStatement::ProcedureCall(call),
}],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("name").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Block(block));
}
#[test]
fn test_block_variant() {
let code = Code::new(
"\
name : block is
begin
end block name;
",
);
let block = BlockStatement {
guard_condition: None,
header: BlockHeader {
generic_clause: None,
generic_map: None,
port_clause: None,
port_map: None,
},
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("name").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Block(block));
}
#[test]
fn test_guarded_block() {
let code = Code::new(
"\
name : block (cond = true)
begin
end block;
",
);
let block = BlockStatement {
guard_condition: Some(code.s1("cond = true").expr()),
header: BlockHeader {
generic_clause: None,
generic_map: None,
port_clause: None,
port_map: None,
},
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("name").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Block(block));
}
#[test]
fn test_guarded_block_variant() {
let code = Code::new(
"\
name : block (cond = true) is
begin
end block;
",
);
let block = BlockStatement {
guard_condition: Some(code.s1("cond = true").expr()),
header: BlockHeader {
generic_clause: None,
generic_map: None,
port_clause: None,
port_map: None,
},
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("name").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Block(block));
}
#[test]
fn test_block_header() {
let code = Code::new(
"\
name: block is
generic(gen: integer := 1);
generic map(gen => 1);
port(prt: integer := 1);
port map(prt => 2);
begin
end block;
",
);
let block = BlockStatement {
guard_condition: None,
header: BlockHeader {
generic_clause: Some(vec![code.s1("gen: integer := 1").generic()]),
generic_map: Some(code.s1("(gen => 1)").association_list()),
port_clause: Some(vec![code.s1("prt: integer := 1").port()]),
port_map: Some(code.s1("(prt => 2)").association_list()),
},
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("name").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Block(block));
}
#[test]
fn test_process_statement() {
let code = Code::new(
"\
process
begin
end process;
",
);
let process = ProcessStatement {
postponed: false,
sensitivity_list: None,
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_process_statement_variant() {
let code = Code::new(
"\
name : process is
begin
end process name;
",
);
let process = ProcessStatement {
postponed: false,
sensitivity_list: None,
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("name").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_postponed_process_statement() {
let code = Code::new(
"\
postponed process
begin
end process;
",
);
let process = ProcessStatement {
postponed: true,
sensitivity_list: None,
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_postponed_process_statement_end_postponed() {
let code = Code::new(
"\
postponed process
begin
end postponed process;
",
);
let process = ProcessStatement {
postponed: true,
sensitivity_list: None,
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_process_statement_end_postponed() {
let code = Code::new(
"\
process is
begin
end postponed process;
",
);
let (stmt, diagnostics) = code.with_stream_diagnostics(parse_labeled_concurrent_statement);
let process = ProcessStatement {
postponed: false,
sensitivity_list: None,
decl: Vec::new(),
statements: Vec::new(),
};
assert_eq!(
diagnostics,
vec![Diagnostic::error(
code.s1("postponed"),
"'postponed' at the end of non-postponed process."
)]
);
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_process_statement_sensitivity() {
let code = Code::new(
"\
process (clk, vec(1)) is
begin
end process;
",
);
let process = ProcessStatement {
postponed: false,
sensitivity_list: Some(SensitivityList::Names(vec![
code.s1("clk").name(),
code.s1("vec(1)").name(),
])),
decl: vec![],
statements: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_process_empty_sensitivity() {
let code = Code::new(
"\
process () is
begin
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())),
decl: Vec::new(),
statements: Vec::new(),
};
assert_eq!(
diagnostics,
vec![Diagnostic::error(
code.s1(")"),
"Processes with sensitivity lists must contain at least one element."
)]
);
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_process_statement_full() {
let code = Code::new(
"\
process (all) is
variable foo : boolean;
begin
foo <= true;
wait;
end process;
",
);
let process = ProcessStatement {
postponed: false,
sensitivity_list: Some(SensitivityList::All),
decl: code.s1("variable foo : boolean;").declarative_part(),
statements: vec![
code.s1("foo <= true;").sequential_statement(),
code.s1("wait;").sequential_statement(),
],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Process(process));
}
#[test]
fn test_concurrent_assert() {
let code = Code::new(
"\
assert cond = true;
",
);
let assert = ConcurrentAssertStatement {
postponed: false,
statement: AssertStatement {
condition: code.s1("cond = true").expr(),
report: None,
severity: None,
},
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Assert(assert));
}
#[test]
fn test_postponed_concurrent_assert() {
let code = Code::new(
"\
postponed assert cond = true;
",
);
let assert = ConcurrentAssertStatement {
postponed: true,
statement: AssertStatement {
condition: code.s1("cond = true").expr(),
report: None,
severity: None,
},
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Assert(assert));
}
#[test]
fn test_concurrent_signal_assignment() {
let code = Code::new(
"\
foo <= bar(2 to 3);
",
);
let assign = ConcurrentSignalAssignment {
postponed: false,
guarded: false,
target: code.s1("foo").name().map_into(Target::Name),
delay_mechanism: None,
rhs: AssignmentRightHand::Simple(code.s1("bar(2 to 3)").waveform()),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Assignment(assign));
}
#[test]
fn test_concurrent_signal_assignment_external_name() {
let code = Code::new(
"\
<< signal dut.foo : std_logic >> <= bar(2 to 3);
",
);
let assign = ConcurrentSignalAssignment {
postponed: false,
guarded: false,
target: code
.s1("<< signal dut.foo : std_logic >>")
.name()
.map_into(Target::Name),
delay_mechanism: None,
rhs: AssignmentRightHand::Simple(code.s1("bar(2 to 3)").waveform()),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(stmt.statement, ConcurrentStatement::Assignment(assign));
}
#[test]
fn parse_selected_signal_assignment() {
let code = Code::new(
"\
with x(0) + 1 select
foo(0) <= transport bar(1,2) after 2 ns when 0|1;",
);
let selection = Selection {
expression: code.s1("x(0) + 1").expr(),
alternatives: vec![Alternative {
choices: code.s1("0|1").choices(),
item: code.s1("bar(1,2) after 2 ns").waveform(),
}],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, None);
assert_eq!(
stmt.statement,
ConcurrentStatement::Assignment(ConcurrentSignalAssignment {
postponed: false,
guarded: false,
target: code.s1("foo(0)").name().map_into(Target::Name),
delay_mechanism: Some(DelayMechanism::Transport),
rhs: AssignmentRightHand::Selected(selection)
})
);
}
#[test]
fn test_component_instantiation() {
let code = Code::new(
"\
inst: component lib.foo.bar;
",
);
let inst = InstantiationStatement {
unit: InstantiatedUnit::Component(code.s1("lib.foo.bar").selected_name()),
generic_map: vec![],
port_map: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("inst").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Instance(inst));
}
#[test]
fn test_configuration_instantiation() {
let code = Code::new(
"\
inst: configuration lib.foo.bar;
",
);
let inst = InstantiationStatement {
unit: InstantiatedUnit::Configuration(code.s1("lib.foo.bar").selected_name()),
generic_map: vec![],
port_map: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("inst").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Instance(inst));
}
#[test]
fn test_entity_instantiation() {
let code = Code::new(
"\
inst: entity lib.foo.bar;
",
);
let inst = InstantiationStatement {
unit: InstantiatedUnit::Entity(code.s1("lib.foo.bar").selected_name(), None),
generic_map: vec![],
port_map: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("inst").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Instance(inst));
}
#[test]
fn test_entity_architecture_instantiation() {
let code = Code::new(
"\
inst: entity lib.foo.bar(arch);
",
);
let inst = InstantiationStatement {
unit: InstantiatedUnit::Entity(
code.s1("lib.foo.bar").selected_name(),
Some(code.s1("arch").ident()),
),
generic_map: vec![],
port_map: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("inst").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Instance(inst));
}
#[test]
fn test_component_aspect_maps() {
let code = Code::new(
"\
inst: component lib.foo.bar
generic map (
const => 1
)
port map (
clk => clk_foo
);
",
);
let inst = InstantiationStatement {
unit: InstantiatedUnit::Component(code.s1("lib.foo.bar").selected_name()),
generic_map: code
.s1("(
const => 1
)")
.association_list(),
port_map: code
.s1("(
clk => clk_foo
)")
.association_list(),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("inst").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Instance(inst));
}
#[test]
fn test_component_no_keyword_port_aspect_map() {
let code = Code::new(
"\
inst: lib.foo.bar
port map (
clk => clk_foo
);
",
);
let inst = InstantiationStatement {
unit: InstantiatedUnit::Component(code.s1("lib.foo.bar").selected_name()),
generic_map: vec![],
port_map: code
.s1("(
clk => clk_foo
)")
.association_list(),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("inst").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Instance(inst));
}
#[test]
fn test_component_no_keyword_generic_aspect_map() {
let code = Code::new(
"\
inst: lib.foo.bar
generic map (
const => 1
);
",
);
let inst = InstantiationStatement {
unit: InstantiatedUnit::Component(code.s1("lib.foo.bar").selected_name()),
generic_map: code
.s1("(
const => 1
)")
.association_list(),
port_map: vec![],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("inst").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::Instance(inst));
}
#[test]
fn test_for_generate_empty() {
let code = Code::new(
"\
gen: for idx in 0 to 1 generate
end generate;
",
);
let gen = ForGenerateStatement {
index_name: code.s1("idx").decl_ident(),
discrete_range: code.s1("0 to 1").discrete_range(),
body: GenerateBody {
alternative_label: None,
decl: None,
statements: vec![],
},
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::ForGenerate(gen));
}
#[test]
fn test_for_generate() {
let code = Code::new(
"\
gen: for idx in 0 to 1 generate
foo <= bar;
end generate;
",
);
let gen = ForGenerateStatement {
index_name: code.s1("idx").decl_ident(),
discrete_range: code.s1("0 to 1").discrete_range(),
body: GenerateBody {
alternative_label: None,
decl: None,
statements: vec![code.s1("foo <= bar;").concurrent_statement()],
},
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::ForGenerate(gen));
}
#[test]
fn test_for_generate_empty_declarations() {
fn test(decl: Option<Vec<Declaration>>, code: Code) {
let gen = ForGenerateStatement {
index_name: code.s1("idx").decl_ident(),
discrete_range: code.s1("0 to 1").discrete_range(),
body: GenerateBody {
alternative_label: None,
decl,
statements: vec![code.s1("foo <= bar;").concurrent_statement()],
},
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::ForGenerate(gen));
}
test(
Some(vec![]),
Code::new(
"\
gen: for idx in 0 to 1 generate
begin
foo <= bar;
end generate;
",
),
);
test(
None,
Code::new(
"\
gen: for idx in 0 to 1 generate
foo <= bar;
end generate;
",
),
);
test(
Some(vec![]),
Code::new(
"\
gen: for idx in 0 to 1 generate
begin
foo <= bar;
end;
end generate;
",
),
);
test(
Some(vec![]),
Code::new(
"\
gen: for idx in 0 to 1 generate
begin
foo <= bar;
end alt_label;
end generate;
",
),
);
}
#[test]
fn test_for_generate_declarations() {
fn test(code: Code) {
let gen = ForGenerateStatement {
index_name: code.s1("idx").decl_ident(),
discrete_range: code.s1("0 to 1").discrete_range(),
body: GenerateBody {
alternative_label: None,
decl: Some(code.s1("signal foo : natural;").declarative_part()),
statements: vec![code.s1("foo <= bar;").concurrent_statement()],
},
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::ForGenerate(gen));
}
test(Code::new(
"\
gen: for idx in 0 to 1 generate
signal foo : natural;
begin
foo <= bar;
end generate;
",
));
test(Code::new(
"\
gen: for idx in 0 to 1 generate
signal foo : natural;
begin
foo <= bar;
end;
end generate;
",
));
test(Code::new(
"\
gen: for idx in 0 to 1 generate
signal foo : natural;
begin
foo <= bar;
end alt_label;
end generate;
",
));
}
#[test]
fn test_if_generate_empty() {
let code = Code::new(
"\
gen: if cond = true generate
end generate;
",
);
let gen = IfGenerateStatement {
conditionals: vec![Conditional {
condition: code.s1("cond = true").expr(),
item: GenerateBody {
alternative_label: None,
decl: None,
statements: vec![],
},
}],
else_item: None,
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::IfGenerate(gen));
}
#[test]
fn test_if_generate_declarative_region() {
let code = Code::new(
"\
gen: if cond = true generate
begin
end generate;
",
);
let gen = IfGenerateStatement {
conditionals: vec![Conditional {
condition: code.s1("cond = true").expr(),
item: GenerateBody {
alternative_label: None,
decl: Some(vec![]),
statements: vec![],
},
}],
else_item: None,
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::IfGenerate(gen));
}
#[test]
fn test_if_elseif_else_generate_empty() {
let code = Code::new(
"\
gen: if cond = true generate
elsif cond2 = true generate
else generate
end generate;
",
);
let gen = IfGenerateStatement {
conditionals: vec![
Conditional {
condition: code.s1("cond = true").expr(),
item: GenerateBody {
alternative_label: None,
decl: None,
statements: vec![],
},
},
Conditional {
condition: code.s1("cond2 = true").expr(),
item: GenerateBody {
alternative_label: None,
decl: None,
statements: vec![],
},
},
],
else_item: Some(GenerateBody {
alternative_label: None,
decl: None,
statements: vec![],
}),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::IfGenerate(gen));
}
#[test]
fn test_if_elseif_else_generate() {
let code = Code::new(
"\
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;
",
);
let gen = IfGenerateStatement {
conditionals: vec![
Conditional {
condition: code.s1("cond = true").expr(),
item: GenerateBody {
alternative_label: None,
decl: Some(code.s1("variable v1 : boolean;").declarative_part()),
statements: vec![code.s1("foo1(clk);").concurrent_statement()],
},
},
Conditional {
condition: code.s1("cond2 = true").expr(),
item: GenerateBody {
alternative_label: None,
decl: Some(code.s1("variable v2 : boolean;").declarative_part()),
statements: vec![code.s1("foo2(clk);").concurrent_statement()],
},
},
],
else_item: Some(GenerateBody {
alternative_label: None,
decl: Some(code.s1("variable v3 : boolean;").declarative_part()),
statements: vec![code.s1("foo3(clk);").concurrent_statement()],
}),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::IfGenerate(gen));
}
#[test]
fn test_if_elseif_else_generate_alternative_label() {
let code = Code::new(
"\
gen: if alt1: cond = true generate
elsif alt2: cond2 = true generate
else alt3: generate
end alt4;
end generate;
",
);
let gen = IfGenerateStatement {
conditionals: vec![
Conditional {
condition: code.s1("cond = true").expr(),
item: GenerateBody {
alternative_label: Some(code.s1("alt1").decl_ident()),
decl: None,
statements: vec![],
},
},
Conditional {
condition: code.s1("cond2 = true").expr(),
item: GenerateBody {
alternative_label: Some(code.s1("alt2").decl_ident()),
decl: None,
statements: vec![],
},
},
],
else_item: Some(GenerateBody {
alternative_label: Some(code.s1("alt3").decl_ident()),
decl: None,
statements: vec![],
}),
};
let (stmt, diagnostics) = code.with_stream_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::IfGenerate(gen));
assert_eq!(
diagnostics,
vec![Diagnostic::error(
code.s1("alt4"),
"End identifier mismatch, expected alt3"
)]
);
}
#[test]
fn test_if_elseif_else_generate_inner_end() {
let code = Code::new(
"\
gen: if alt1: cond = true generate
end alt1;
elsif alt2: cond2 = true generate
end alt2;
else alt3: generate
end alt3;
end generate;
",
);
let gen = IfGenerateStatement {
conditionals: vec![
Conditional {
condition: code.s1("cond = true").expr(),
item: GenerateBody {
alternative_label: Some(code.s1("alt1").decl_ident()),
decl: None,
statements: vec![],
},
},
Conditional {
condition: code.s1("cond2 = true").expr(),
item: GenerateBody {
alternative_label: Some(code.s1("alt2").decl_ident()),
decl: None,
statements: vec![],
},
},
],
else_item: Some(GenerateBody {
alternative_label: Some(code.s1("alt3").decl_ident()),
decl: None,
statements: vec![],
}),
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::IfGenerate(gen));
}
#[test]
fn test_case_generate() {
let code = Code::new(
"\
gen: case expr(0) + 2 generate
when 1 | 2 =>
sig <= value;
when others =>
foo(clk);
end generate;
",
);
let gen = CaseGenerateStatement {
expression: code.s1("expr(0) + 2").expr(),
alternatives: vec![
Alternative {
choices: code.s1("1 | 2").choices(),
item: GenerateBody {
alternative_label: None,
decl: None,
statements: vec![code.s1("sig <= value;").concurrent_statement()],
},
},
Alternative {
choices: code.s1("others").choices(),
item: GenerateBody {
alternative_label: None,
decl: None,
statements: vec![code.s1("foo(clk);").concurrent_statement()],
},
},
],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::CaseGenerate(gen));
}
#[test]
fn test_case_alternative_label() {
let code = Code::new(
"\
gen: case expr(0) + 2 generate
when alt1: 1 | 2 =>
sig <= value;
when alt2: others =>
foo(clk);
end generate;
",
);
let gen = CaseGenerateStatement {
expression: code.s1("expr(0) + 2").expr(),
alternatives: vec![
Alternative {
choices: code.s1("1 | 2").choices(),
item: GenerateBody {
alternative_label: Some(code.s1("alt1").decl_ident()),
decl: None,
statements: vec![code.s1("sig <= value;").concurrent_statement()],
},
},
Alternative {
choices: code.s1("others").choices(),
item: GenerateBody {
alternative_label: Some(code.s1("alt2").decl_ident()),
decl: None,
statements: vec![code.s1("foo(clk);").concurrent_statement()],
},
},
],
};
let stmt = code.with_stream_no_diagnostics(parse_labeled_concurrent_statement);
assert_eq!(stmt.label, Some(code.s1("gen").decl_ident()));
assert_eq!(stmt.statement, ConcurrentStatement::CaseGenerate(gen));
}
}