Support prefix labels on procedural blocks
SystemVerilog sequential and parallel blocks allow a block identifier before
the `begin` or `fork` keyword:
LABEL: begin
statement;
end
LABEL: fork
statement;
join
The parser currently only accepts the block identifier after `begin` and
`fork`. Add a prefix-label rule using `identifier_name`. This accepts labels
returned as either `IDENTIFIER` or `TYPE_IDENTIFIER` without duplicating the
label grammar. The mixed procedural item list lets the parser use the following
`:` to distinguish a visible type identifier used as a label from the start of
a variable declaration.
Use a shared optional prefix rule for sequential and parallel block forms.
Resolve the prefix and post-keyword names before starting the common block path,
and bind attributes placed between the label and block keyword. Require
SystemVerilog mode when a prefix label is present.
IEEE 1800-2023 section 9.3.5 does not allow a prefix label and a block name
after `begin` or `fork` at the same time. Report an error for this form while
keeping it in the grammar for error recovery. Keep the existing closing label
handling, which allows a matching name after `end` or a join keyword.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
parent
dcc8e93d7e
commit
fd902d1501
78
parse.y
78
parse.y
|
|
@ -603,6 +603,29 @@ static PBlock *pform_start_block(const YYLTYPE&loc, const char *name,
|
|||
return block;
|
||||
}
|
||||
|
||||
static char *pform_start_block_with_labels(
|
||||
const YYLTYPE&loc, const YYLTYPE&name_loc,
|
||||
char *prefix_label, char *block_name,
|
||||
std::list<named_pexpr_t> *attributes,
|
||||
PBlock::BL_TYPE block_type)
|
||||
{
|
||||
std::unique_ptr<char[]> prefix_label_ptr(prefix_label);
|
||||
std::unique_ptr<char[]> block_name_ptr(block_name);
|
||||
|
||||
if (prefix_label_ptr && block_name_ptr) {
|
||||
yyerror(name_loc,
|
||||
"error: A block cannot have both a prefix label and a block name.");
|
||||
}
|
||||
|
||||
const char *name = block_name_ptr ? block_name_ptr.get()
|
||||
: prefix_label_ptr.get();
|
||||
auto block = pform_start_block(loc, name, block_type);
|
||||
pform_bind_attributes(block->attributes, attributes);
|
||||
|
||||
return block_name_ptr ? block_name_ptr.release()
|
||||
: prefix_label_ptr.release();
|
||||
}
|
||||
|
||||
static PBlock *pform_finish_block(const YYLTYPE&block_loc,
|
||||
const YYLTYPE&end_loc, const char *type,
|
||||
char *raw_name, char *end_label,
|
||||
|
|
@ -798,6 +821,11 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
|
|||
std::vector<Statement*>*statement_list;
|
||||
struct procedural_item_list_t *procedural_item_list;
|
||||
|
||||
struct {
|
||||
char *label;
|
||||
std::list<named_pexpr_t> *attributes;
|
||||
} block_prefix;
|
||||
|
||||
decl_assignment_t*decl_assignment;
|
||||
std::list<decl_assignment_t*>*decl_assignments;
|
||||
|
||||
|
|
@ -1088,6 +1116,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
|
|||
%type <procedural_item_list> block_item_or_statement_list_opt
|
||||
%type <procedural_item_list> tf_item_or_statement_list
|
||||
%type <procedural_item_list> tf_item_or_statement_list_opt
|
||||
%type <block_prefix> block_prefix_opt
|
||||
%type <text> sequential_block_start parallel_block_start
|
||||
|
||||
%type <statement> analog_statement
|
||||
|
||||
|
|
@ -7315,6 +7345,34 @@ subroutine_call
|
|||
}
|
||||
;
|
||||
|
||||
block_prefix_opt
|
||||
: /* empty */
|
||||
{ $$.label = nullptr;
|
||||
$$.attributes = nullptr;
|
||||
}
|
||||
| identifier_name ':' attribute_list_opt
|
||||
{ pform_requires_sv(@1, "Block prefix label");
|
||||
$$.label = $1;
|
||||
$$.attributes = $3;
|
||||
}
|
||||
;
|
||||
|
||||
sequential_block_start
|
||||
: block_prefix_opt K_begin label_opt
|
||||
{ $$ = pform_start_block_with_labels(@2, @3, $1.label, $3,
|
||||
$1.attributes, PBlock::BL_SEQ);
|
||||
@$ = @2;
|
||||
}
|
||||
;
|
||||
|
||||
parallel_block_start
|
||||
: block_prefix_opt K_fork label_opt
|
||||
{ $$ = pform_start_block_with_labels(@2, @3, $1.label, $3,
|
||||
$1.attributes, PBlock::BL_PAR);
|
||||
@$ = @2;
|
||||
}
|
||||
;
|
||||
|
||||
statement_item /* This is roughly statement_item in the LRM */
|
||||
|
||||
/* assign and deassign statements are procedural code to do
|
||||
|
|
@ -7356,14 +7414,12 @@ statement_item /* This is roughly statement_item in the LRM */
|
|||
the declarations. The scope is popped at the end of the block. */
|
||||
|
||||
/* In SystemVerilog an unnamed block can contain variable declarations. */
|
||||
| K_begin label_opt
|
||||
{ pform_start_block(@1, $2, PBlock::BL_SEQ); }
|
||||
block_item_or_statement_list_opt K_end label_opt
|
||||
{ if (!$2 && $4->has_decls) {
|
||||
| sequential_block_start block_item_or_statement_list_opt K_end label_opt
|
||||
{ if (!$1 && $2->has_decls) {
|
||||
pform_block_decls_requires_sv();
|
||||
}
|
||||
$$ = pform_finish_block(@1, @6, "block", $2, $6,
|
||||
PBlock::BL_SEQ, $4);
|
||||
$$ = pform_finish_block(@1, @4, "block", $1, $4,
|
||||
PBlock::BL_SEQ, $2);
|
||||
}
|
||||
|
||||
/* fork-join blocks are very similar to begin-end blocks. In fact,
|
||||
|
|
@ -7372,13 +7428,11 @@ statement_item /* This is roughly statement_item in the LRM */
|
|||
code generator can do the right thing. */
|
||||
|
||||
/* In SystemVerilog an unnamed block can contain variable declarations. */
|
||||
| K_fork label_opt
|
||||
{ pform_start_block(@1, $2, PBlock::BL_PAR); }
|
||||
block_item_or_statement_list_opt join_keyword label_opt
|
||||
{ if (!$2 && $4->has_decls) {
|
||||
pform_requires_sv(@4, "Variable declaration in unnamed block");
|
||||
| parallel_block_start block_item_or_statement_list_opt join_keyword label_opt
|
||||
{ if (!$1 && $2->has_decls) {
|
||||
pform_requires_sv(@2, "Variable declaration in unnamed block");
|
||||
}
|
||||
$$ = pform_finish_block(@1, @6, "fork", $2, $6, $5, $4);
|
||||
$$ = pform_finish_block(@1, @4, "fork", $1, $4, $3, $2);
|
||||
}
|
||||
|
||||
| K_disable hierarchy_identifier ';'
|
||||
|
|
|
|||
Loading…
Reference in New Issue