diff --git a/docs/guide/extensions.rst b/docs/guide/extensions.rst index 1e8c629f7..5cb0cf572 100644 --- a/docs/guide/extensions.rst +++ b/docs/guide/extensions.rst @@ -25,6 +25,12 @@ or "`ifdef`"'s may break other tools. This will report an error when encountered, like C++'s #error. +.. option:: """ [string] """ + + A triple-quoted block specifies a string which may include newlines and + single quotes. This extension is experimental and may be removed + without deprecation. + .. option:: $c([string], ...); The string will be embedded directly in the output C++ code at the point diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index 8d495716a..0c0fbf969 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -298,7 +298,7 @@ string V3ParseGrammar::deQuote(FileLine* fileline, string text) { } else if (*cp == '\\') { quoted = true; octal_digits = 0; - } else if (*cp != '"') { + } else { newtext += *cp; } } diff --git a/src/V3PreLex.l b/src/V3PreLex.l index bc1dda03d..e423341f2 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -65,23 +65,24 @@ static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t, l) /**********************************************************************/ %} -%x CMTONEM +%x ARGMODE %x CMTBEGM %x CMTMODE -%x STRMODE -%x DEFFPAR -%x DEFFORM -%x DEFVAL +%x CMTONEM %x DEFCMT -%x STRIFY -%x ARGMODE +%x DEFFORM +%x DEFFPAR +%x DEFVAL +%x ENCBASE64 %x INCMODE -%x PRTMODE %x PRAGMA %x PRAGMAERR %x PRAGMAPRT %x PRAGMAPRTERR -%x ENCBASE64 +%x PRTMODE +%x QQQMODE +%x STRIFY +%x STRMODE /* drop: Drop Ctrl-Z - can't pass thru or may EOF the output too soon */ @@ -262,6 +263,23 @@ bom [\357\273\277] yyleng=0; FL_BRK; } else return VP_STRING; } + /* Pass-through quote-quote-quote */ +{quote}{quote}{quote} { yy_push_state(QQQMODE); yymore(); } +<> { FL_FWDC; linenoInc(); yyerrorf("EOF in unterminated \"\"\" string"); + yyleng=0; return VP_EOF_ERROR; } +{crnl} { FL_FWDC; linenoInc(); yymore(); } +{word} { yymore(); } +[^\"\\] { yymore(); } +[\\]{crnl} { linenoInc(); yymore(); } +[\\]{wsn}+{crnl} { LEXP->warnBackslashSpace(); yyless(1); } +[\\]. { yymore(); } +. { yymore(); } +{quote}{quote}{quote} { FL_FWDC; yy_pop_state(); + if (LEXP->m_parenLevel || LEXP->m_defQuote) { + LEXP->m_defQuote=false; appendDefValue(yytext, yyleng); + yyleng=0; FL_BRK; + } else return VP_STRING; } + /* Stringification */ {tickquote} { FL_FWDC; yy_push_state(STRIFY); return VP_STRIFY; } <> { FL_FWDC; linenoInc(); yyerrorf("EOF in unterminated '\""); @@ -319,6 +337,7 @@ bom [\357\273\277] [\\]{wsn}+{crnl} { LEXP->warnBackslashSpace(); yyless(1); } [\\]{crnl} { FL_FWDC; linenoInc(); appendDefValue((char*)"\\\n", 2); FL_BRK; } /* Include return so can maintain output line count */ {quote} { LEXP->m_defQuote=true; yy_push_state(STRMODE); yymore(); } /* Legal only in default values */ +{quote}{quote}{quote} { LEXP->m_defQuote=true; yy_push_state(QQQMODE); yymore(); } /* Legal only in default values */ "`\\`\"" { FL_FWDC; appendDefValue(yytext, yyleng); FL_BRK; } /* Maybe illegal, otherwise in default value */ {tickquote} { FL_FWDC; appendDefValue(yytext, yyleng); FL_BRK; } /* Maybe illegal, otherwise in default value */ [{\[] { FL_FWDC; LEXP->m_formalLevel++; appendDefValue(yytext, yyleng); FL_BRK; } @@ -337,6 +356,7 @@ bom [\357\273\277] [\\]{wsn}+{crnl} { LEXP->warnBackslashSpace(); yyless(1); } [\\]{crnl} { FL_FWDC; linenoInc(); appendDefValue((char*)"\\\n", 2); FL_BRK; } /* Return, AND \ is part of define value */ {quote} { LEXP->m_defQuote = true; yy_push_state(STRMODE); yymore(); } +{quote}{quote}{quote} { LEXP->m_defQuote = true; yy_push_state(QQQMODE); yymore(); } [^\/\*\n\r\\\"]+ | [\\][^\n\r] | . { FL_FWDC; appendDefValue(yytext, yyleng); FL_BRK; } @@ -364,6 +384,7 @@ bom [\357\273\277] yyleng = 0; return VP_EOF_ERROR; } {crnl} { FL_FWDC; linenoInc(); yytext=(char*)"\n"; yyleng=1; return VP_WHITE; } {quote} { yy_push_state(STRMODE); yymore(); } +{quote}{quote}{quote} { yy_push_state(QQQMODE); yymore(); } "`\\`\"" { FL_FWDC; appendDefValue(yytext, yyleng); FL_BRK; } /* Literal text */ {tickquote} { FL_FWDC; yy_push_state(STRIFY); return VP_STRIFY; } [{\[] { FL_FWDC; LEXP->m_parenLevel++; appendDefValue(yytext, yyleng); FL_BRK; } diff --git a/src/verilog.l b/src/verilog.l index b5cdd53ca..96e1d5718 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -79,7 +79,7 @@ static double lexParseDouble(FileLine* fl, const char* textp, size_t length) { %o 25000 %s V95 V01NC V01C V05 S05 S09 S12 S17 -%s STRING ATTRMODE TABLE +%s ATTRMODE QQQ STRING TABLE %s VA5 SAX VLT %s SYSCHDR SYSCINT SYSCIMP SYSCIMPH SYSCCTOR SYSCDTOR %s IGNORE @@ -886,6 +886,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} return yaSTRING; } \" { yy_push_state(STRING); yymore(); } + \"\"\" { yy_push_state(QQQ); yymore(); } {vnum} { /* "# 1'b0" is a delay value so must lex as "#" "1" "'b0" */ if (PARSEP->lexPrevToken()=='#') { @@ -940,6 +941,19 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} {word} { yymore(); } . { yymore(); } + /************************************************************************/ + /* """ */ +<> { FL; yylval.fl->v3error("EOF in unterminated \"\"\" string"); + yyleng = 0; yy_pop_state(); FL_BRK; yyterminate(); } +\\. { yymore(); } +\n { yymore(); } +\"\"\" { yy_pop_state(); + FL; yylval.strp = PARSEP->newString(yytext + 3, yyleng - 6); + return yaSTRING; } +\" { yymore(); } +{word} { yymore(); } +. { yymore(); } + /************************************************************************/ /* Attributes */ {crnl} { yymore(); } diff --git a/test_regress/t/t_display_qqq.out b/test_regress/t/t_display_qqq.out new file mode 100644 index 000000000..de4991997 --- /dev/null +++ b/test_regress/t/t_display_qqq.out @@ -0,0 +1,5 @@ +First "quoted" +second +third +fourth +*-* All Finished *-* diff --git a/test_regress/t/t_display_qqq.pl b/test_regress/t/t_display_qqq.pl new file mode 100755 index 000000000..3db494e80 --- /dev/null +++ b/test_regress/t/t_display_qqq.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +compile( + ); + +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); + +1; diff --git a/test_regress/t/t_display_qqq.v b/test_regress/t/t_display_qqq.v new file mode 100644 index 000000000..1b5568269 --- /dev/null +++ b/test_regress/t/t_display_qqq.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + + initial begin + $display("""First "quoted"\nsecond\ +third +fourth"""); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_preproc.out b/test_regress/t/t_preproc.out index 34e54dbe2..1aa204271 100644 --- a/test_regress/t/t_preproc.out +++ b/test_regress/t/t_preproc.out @@ -1005,6 +1005,20 @@ endmodule `line 695 "t/t_preproc.v" 0 + +"""First line with "quoted"\nSecond line\ +Third line""" +"""First line +Second line""" + +`line 702 "t/t_preproc.v" 0 + + +"""QQQ defform""" +"""QQQ defval""" + +`line 707 "t/t_preproc.v" 0 + predef 0 0 @@ -1026,4 +1040,4 @@ predef 2 2 -`line 717 "t/t_preproc.v" 2 +`line 729 "t/t_preproc.v" 2 diff --git a/test_regress/t/t_preproc.v b/test_regress/t/t_preproc.v index c804ad797..527bea0d5 100644 --- a/test_regress/t/t_preproc.v +++ b/test_regress/t/t_preproc.v @@ -692,6 +692,18 @@ endmodule `define stringify(text) `"text`" `stringify(`NOT_DEFINED_STR) +//====================================================================== + +"""First line with "quoted"\nSecond line\ +Third line""" +"""First line +Second line""" + +`define QQQ """QQQ defform""" +`define QQQS(x) x +`QQQ +`QQQS("""QQQ defval""") + //====================================================================== // IEEE mandated predefines `undefineall // undefineall should have no effect on these diff --git a/test_regress/t/t_preproc_comments.out b/test_regress/t/t_preproc_comments.out index 7777d4332..4c6a565ab 100644 --- a/test_regress/t/t_preproc_comments.out +++ b/test_regress/t/t_preproc_comments.out @@ -1010,6 +1010,20 @@ endmodule `line 695 "t/t_preproc.v" 0 //====================================================================== + +"""First line with "quoted"\nSecond line\ +Third line""" +"""First line +Second line""" + +`line 702 "t/t_preproc.v" 0 + + +"""QQQ defform""" +"""QQQ defval""" + +`line 707 "t/t_preproc.v" 0 +//====================================================================== // IEEE mandated predefines // undefineall should have no effect on these predef 0 0 @@ -1031,4 +1045,4 @@ predef 2 2 // After `undefineall above, for testing --dump-defines -`line 717 "t/t_preproc.v" 2 +`line 729 "t/t_preproc.v" 2 diff --git a/test_regress/t/t_preproc_eof6_bad.out b/test_regress/t/t_preproc_eof6_bad.out new file mode 100644 index 000000000..7449bf833 --- /dev/null +++ b/test_regress/t/t_preproc_eof6_bad.out @@ -0,0 +1,2 @@ +%Error: t/t_preproc_eof6_bad.v:10:1: EOF in unterminated """ string +%Error: Exiting due to diff --git a/test_regress/t/t_preproc_eof6_bad.pl b/test_regress/t/t_preproc_eof6_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_preproc_eof6_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(linter => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_preproc_eof6_bad.v b/test_regress/t/t_preproc_eof6_bad.v new file mode 100644 index 000000000..6f936b832 --- /dev/null +++ b/test_regress/t/t_preproc_eof6_bad.v @@ -0,0 +1,7 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +"""str