From: Nick Gasson Date: Tue, 14 Jan 2025 20:45:55 +0000 (+0000) Subject: Parse Verilog for loops X-Git-Url: https://git.nickg.me.uk/nvc.git/commitdiff_plain Parse Verilog for loops --- diff --git a/src/lexer.l b/src/lexer.l index 6b17ac23..3fd2e005 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -1,7 +1,7 @@ /* -*- mode: c; c-basic-offset: 3 -*- */ /* - * Copyright (C) 2011-2024 Nick Gasson + * Copyright (C) 2011-2025 Nick Gasson * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -662,6 +662,9 @@ BEFORE ?i:before "(*" { return tATTRBEGIN; } "*)" { return tATTREND; } "%" { return tPERCENT; } +"++" { return tPLUSPLUS; } +"--" { return tMINUSMINUS; } +"var" { return tVAR; } "specify" { return tSPECIFY; } "endspecify" { return tENDSPECIFY; } "primitive" { return tPRIMITIVE; } @@ -703,6 +706,7 @@ BEFORE ?i:before "endfunction" { return tENDFUNCTION; } "wait" { return tWAIT; } "parameter" { return tPARAMETER; } +"for" { return tFOR; } {SYSTASK} { yylval.str = xstrdup(yytext); return tSYSTASK; } {VLOG_ID} { yylval.str = xstrdup(yytext); return tID; } diff --git a/src/nvc.c b/src/nvc.c index d26a527a..32a5f332 100644 --- a/src/nvc.c +++ b/src/nvc.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2011-2024 Nick Gasson +// Copyright (C) 2011-2025 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -73,7 +73,7 @@ typedef struct { } cmd_state_t; const char copy_string[] = - "Copyright (C) 2011-2024 Nick Gasson\n" + "Copyright (C) 2011-2025 Nick Gasson\n" "This program comes with ABSOLUTELY NO WARRANTY. This is free software, " "and\nyou are welcome to redistribute it under certain conditions. See " "the GNU\nGeneral Public Licence for details."; diff --git a/src/scan.c b/src/scan.c index 5561bec8..de0f9ce6 100644 --- a/src/scan.c +++ b/src/scan.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2024 Nick Gasson +// Copyright (C) 2014-2025 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -262,7 +262,8 @@ const char *token_str(token_t tok) "before!", "before_", "before!_", "|->", "|=>", "next", "inf", "repeat", "do", "endpoint", "<<", ">>", "<<<", ">>>", "task", "endtask", "endfunction", "`begin_keywords", "`end_keywords", "real", - "shortreal", "realtime", "`__nvc_push", "`__nvc_pop", + "shortreal", "realtime", "`__nvc_push", "`__nvc_pop", "++", "--", + "var", }; if (tok >= 200 && tok - 200 < ARRAY_LEN(token_strs)) diff --git a/src/scan.h b/src/scan.h index d7a24009..4bd27c07 100644 --- a/src/scan.h +++ b/src/scan.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2024 Nick Gasson +// Copyright (C) 2022-2025 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -424,5 +424,8 @@ bool is_scanned_as_psl(void); #define tREALTIME 521 #define tNVCPUSH 522 #define tNVCPOP 523 +#define tPLUSPLUS 524 +#define tMINUSMINUS 525 +#define tVAR 526 #endif // _SCAN_H diff --git a/src/vlog/vlog-node.c b/src/vlog/vlog-node.c index 7650c186..75f815ba 100644 --- a/src/vlog/vlog-node.c +++ b/src/vlog/vlog-node.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2024 Nick Gasson +// Copyright (C) 2022-2025 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -174,6 +174,21 @@ static const imask_t has_map[V_LAST_NODE_KIND] = { // V_CONCAT (I_PARAMS), + + // V_FOR_LOOP + (I_LEFT | I_VALUE | I_RIGHT | I_STMTS), + + // V_FOR_INIT + (I_DECLS | I_STMTS), + + // V_FOR_STEP + (I_STMTS), + + // V_PREFIX + (I_TARGET | I_SUBKIND), + + // V_POSTFIX + (I_TARGET | I_SUBKIND), }; static const char *kind_text_map[V_LAST_NODE_KIND] = { @@ -189,7 +204,8 @@ static const char *kind_text_map[V_LAST_NODE_KIND] = { "V_UNION_DECL", "V_STRUCT_DECL", "V_EVENT_CONTROL", "V_EMPTY", "V_REPEAT", "V_WHILE", "V_DO_WHILE", "V_TASK_DECL", "V_FUNC_DECL", "V_WAIT", "V_PARAM_DECL", "V_COND_EXPR", - "V_REAL", "V_CONCAT", + "V_REAL", "V_CONCAT", "V_FOR_LOOP", "V_FOR_INIT", + "V_FOR_STEP", "V_PREFIX", "V_POSTFIX", }; static const change_allowed_t change_allowed[] = { diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index f65d1a06..7c5d7518 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2024 Nick Gasson +// Copyright (C) 2022-2025 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -94,6 +94,11 @@ typedef enum { V_COND_EXPR, V_REAL, V_CONCAT, + V_FOR_LOOP, + V_FOR_INIT, + V_FOR_STEP, + V_PREFIX, + V_POSTFIX, V_LAST_NODE_KIND } vlog_kind_t; @@ -149,6 +154,11 @@ typedef enum { V_UNARY_IDENTITY, } vlog_unary_t; +typedef enum { + V_INCDEC_PLUS, + V_INCDEC_MINUS, +} vlog_incdec_t; + typedef enum { V_ASSIGN_EQUALS, } vlog_assign_t; diff --git a/src/vlog/vlog-parse.c b/src/vlog/vlog-parse.c index da33e4d0..76315ef0 100644 --- a/src/vlog/vlog-parse.c +++ b/src/vlog/vlog-parse.c @@ -102,6 +102,7 @@ static vlog_node_t p_constant_expression(void); static vlog_node_t p_data_type(void); static void p_list_of_variable_decl_assignments(vlog_node_t parent, vlog_node_t datatype); +static vlog_node_t p_variable_lvalue(void); static inline void _pop_state(const rule_state_t *r) { @@ -1267,6 +1268,34 @@ static vlog_unary_t p_unary_operator(void) } } +static vlog_incdec_t p_inc_or_dec_operator(void) +{ + // ++ | -- + + BEGIN("inc or dec operator"); + + switch (one_of(tPLUSPLUS, tMINUSMINUS)) { + case tMINUSMINUS: return V_INCDEC_MINUS; + case tPLUSPLUS: + default: return V_INCDEC_PLUS; + } +} + +static vlog_node_t p_inc_or_dec_expression(vlog_node_t head) +{ + // inc_or_dec_operator { attribute_instance } variable_lvalue + // | variable_lvalue { attribute_instance } inc_or_dec_operator + + BEGIN_WITH_HEAD("inc or dec expression", head); + + vlog_node_t v = vlog_new(head ? V_POSTFIX : V_PREFIX); + vlog_set_subkind(v, p_inc_or_dec_operator()); + vlog_set_target(v, head ?: p_variable_lvalue()); + + vlog_set_loc(v, CURRENT_LOC); + return v; +} + static vlog_node_t p_nonbinary_expression(void) { // primary | unary_operator { attribute_instance } primary @@ -1293,9 +1322,12 @@ static vlog_node_t p_nonbinary_expression(void) vlog_set_loc(v, CURRENT_LOC); return v; } + case tPLUSPLUS: + case tMINUSMINUS: + return p_inc_or_dec_expression(NULL); default: one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tREAL, tSYSTASK, tLPAREN, - tLBRACE, tMINUS, tTILDE, tBANG); + tLBRACE, tMINUS, tTILDE, tBANG, tPLUSPLUS, tMINUSMINUS); return p_select(error_marker()); } } @@ -1723,6 +1755,116 @@ static vlog_node_t p_conditional_statement(void) return v; } +static vlog_node_t p_variable_assignment(vlog_kind_t kind) +{ + // variable_lvalue = expression + + BEGIN("variable assignment"); + + vlog_node_t v = vlog_new(kind); + vlog_set_target(v, p_variable_lvalue()); + + consume(tEQ); + + vlog_set_value(v, p_expression()); + + vlog_set_loc(v, CURRENT_LOC); + return v; +} + +static void p_list_of_variable_assignments(vlog_node_t parent) +{ + // variable_assignment { , variable_assignment } + + BEGIN("list of variable assignments"); + + do { + vlog_node_t v = p_variable_assignment(V_BASSIGN); + vlog_add_stmt(parent, v); + } while (optional(tCOMMA)); +} + +static void p_for_variable_declaration(vlog_node_t parent) +{ + // [ var ] data_type variable_identifier = expression + // { , variable_identifier = expression } + + BEGIN("for variable declaration"); + + optional(tVAR); + + vlog_node_t dt = p_data_type(); + + do { + vlog_node_t v = vlog_new(V_VAR_DECL); + vlog_set_ident(v, p_identifier()); + vlog_set_type(v, dt); + + consume(tEQ); + + vlog_set_value(v, p_expression()); + + vlog_set_loc(v, CURRENT_LOC); + vlog_add_decl(parent, v); + } while (optional(tCOMMA)); +} + +static vlog_node_t p_for_initialization(void) +{ + // list_of_variable_assignments + // | for_variable_declaration { , for_variable_declaration } + + BEGIN("for initialization"); + + vlog_node_t v = vlog_new(V_FOR_INIT); + + if (scan(tREG, tSTRUCT, tUNION, tENUM, tSVINT, tINTEGER, tSVREAL, + tSHORTREAL, tREALTIME, tLOGIC, tVAR)) { + do { + p_for_variable_declaration(v); + } while (optional(tCOMMA)); + } + else + p_list_of_variable_assignments(v); + + vlog_set_loc(v, CURRENT_LOC); + return v; +} + +static vlog_node_t p_for_step(void) +{ + // operator_assignment | inc_or_dec_expression | function_subroutine_call + + BEGIN("for step"); + + vlog_node_t v = vlog_new(V_FOR_STEP); + + switch (peek()) { + case tPLUSPLUS: + case tMINUSMINUS: + vlog_add_stmt(v, p_inc_or_dec_expression(NULL)); + break; + default: + { + vlog_node_t head = p_variable_lvalue(); + + switch (peek()) { + case tPLUSPLUS: + case tMINUSMINUS: + vlog_add_stmt(v, p_inc_or_dec_expression(head)); + break; + default: + vlog_add_stmt(v, p_operator_assignment(head)); + break; + } + } + break; + } + + vlog_set_loc(v, CURRENT_LOC); + return v; +} + static vlog_node_t p_loop_statement(void) { // forever statement_or_null @@ -1736,7 +1878,7 @@ static vlog_node_t p_loop_statement(void) BEGIN("loop statement"); - switch (one_of(tFOREVER, tWHILE, tREPEAT, tDO)) { + switch (one_of(tFOREVER, tWHILE, tREPEAT, tDO, tFOR)) { case tFOREVER: { vlog_node_t v = vlog_new(V_FOREVER); @@ -1800,6 +1942,39 @@ static vlog_node_t p_loop_statement(void) return v; } + case tFOR: + { + vlog_node_t v = vlog_new(V_FOR_LOOP); + + consume(tLPAREN); + + if (not_at_token(tSEMI)) + vlog_set_left(v, p_for_initialization()); + else + vlog_set_left(v, vlog_new(V_FOR_INIT)); + + consume(tSEMI); + + if (not_at_token(tSEMI)) + vlog_set_value(v, p_expression()); + + consume(tSEMI); + + if (not_at_token(tRPAREN)) + vlog_set_right(v, p_for_step()); + else + vlog_set_right(v, vlog_new(V_FOR_STEP)); + + consume(tRPAREN); + + vlog_node_t s = p_statement_or_null(); + if (s != NULL) + vlog_add_stmt(v, s); + + vlog_set_loc(v, CURRENT_LOC); + return v; + } + default: should_not_reach_here(); } @@ -1872,12 +2047,13 @@ static vlog_node_t p_statement_item(void) case tWHILE: case tREPEAT: case tDO: + case tFOR: return p_loop_statement(); case tWAIT: return p_wait_statement(); default: one_of(tID, tAT, tHASH, tBEGIN, tSYSTASK, tIF, tFOREVER, tWHILE, tREPEAT, - tDO, tWAIT); + tDO, tFOR, tWAIT); drop_tokens_until(tSEMI); return NULL; } diff --git a/src/vlog/vlog-sem.c b/src/vlog/vlog-sem.c index 28513c64..431ce482 100644 --- a/src/vlog/vlog-sem.c +++ b/src/vlog/vlog-sem.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2024 Nick Gasson +// Copyright (C) 2022-2025 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -650,6 +650,34 @@ static void vlog_check_concat(vlog_node_t expr) } } +static void vlog_check_for_loop(vlog_node_t v) +{ + vlog_check(vlog_left(v)); + vlog_check(vlog_right(v)); + + const int nstmts = vlog_stmts(v); + for (int i = 0; i < nstmts; i++) + vlog_check(vlog_stmt(v, i)); +} + +static void vlog_check_for_init(vlog_node_t v) +{ + const int ndecls = vlog_decls(v); + for (int i = 0; i < ndecls; i++) + vlog_check(vlog_decl(v, i)); + + const int nstmts = vlog_stmts(v); + for (int i = 0; i < nstmts; i++) + vlog_check(vlog_stmt(v, i)); +} + +static void vlog_check_for_step(vlog_node_t v) +{ + const int nstmts = vlog_stmts(v); + for (int i = 0; i < nstmts; i++) + vlog_check(vlog_stmt(v, i)); +} + void vlog_check(vlog_node_t v) { switch (vlog_kind(v)) { @@ -769,6 +797,15 @@ void vlog_check(vlog_node_t v) case V_CONCAT: vlog_check_concat(v); break; + case V_FOR_LOOP: + vlog_check_for_loop(v); + break; + case V_FOR_INIT: + vlog_check_for_init(v); + break; + case V_FOR_STEP: + vlog_check_for_step(v); + break; default: fatal_at(vlog_loc(v), "cannot check verilog node %s", vlog_kind_str(vlog_kind(v))); diff --git a/test/test_vlog.c b/test/test_vlog.c index 46ee355c..3bd0a3ec 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -169,7 +169,7 @@ START_TEST(test_parse1) vlog_node_t m = vlog_parse(); fail_if(m == NULL); fail_unless(vlog_kind(m) == V_MODULE); - fail_unless(vlog_stmts(m) == 14); + fail_unless(vlog_stmts(m) == 15); fail_unless(vlog_ports(m) == 0); fail_unless(vlog_decls(m) == 14); diff --git a/test/vlog/parse1.v b/test/vlog/parse1.v index d0215544..e1a943d3 100644 --- a/test/vlog/parse1.v +++ b/test/vlog/parse1.v @@ -56,4 +56,11 @@ module parse1; realtime r5 = 1.0; assign r2 = {x, y}; assign {r1, r2} = {x, y}; + initial begin + for (r1 = 1; r1 < 5; r1++) + x = x + 1; + for (;;r2 = r2 * x); + for (int i = 0; i > 0; --i); + for (var reg x = 5;;); + end endmodule // parse1