From e7cbefa31608733201f7e2fd444cfe8486827f79 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 21 Dec 2009 08:54:39 -0500 Subject: [PATCH] Support 1800-2009 defines with default arguments. --- Changes | 2 +- src/V3PreLex.h | 2 + src/V3PreLex.l | 63 +++++++------- src/V3PreProc.cpp | 134 ++++++++++++++++++----------- src/V3Tristate.cpp | 8 +- test_regress/t/t_preproc_def09.out | 75 ++++++++++++++++ test_regress/t/t_preproc_def09.pl | 23 +++++ test_regress/t/t_preproc_def09.v | 73 ++++++++++++++++ 8 files changed, 293 insertions(+), 87 deletions(-) create mode 100644 test_regress/t/t_preproc_def09.out create mode 100755 test_regress/t/t_preproc_def09.pl create mode 100644 test_regress/t/t_preproc_def09.v diff --git a/Changes b/Changes index cf4f8f150..23f12f353 100644 --- a/Changes +++ b/Changes @@ -22,7 +22,7 @@ indicates the contributor was also the author of the fix; Thanks! *** Support $sformat and $swrite. -*** Support `undefineall. +*** Support 1800-2009 define defaults and `undefineall. *** Add VARHIDDEN warning when signal name hides module name. diff --git a/src/V3PreLex.h b/src/V3PreLex.h index 818a9c2c0..33d2b4feb 100644 --- a/src/V3PreLex.h +++ b/src/V3PreLex.h @@ -116,6 +116,7 @@ class V3PreLex { bool m_pedantic; // Obey standard; don't Substitute `__FILE__ and `__LINE__ // State from lexer + int m_formalLevel; // Parenthesis counting inside def formals int m_parenLevel; // Parenthesis counting inside def args int m_pslParenLevel;// PSL Parenthesis (){} counting, so we can find final ; bool m_pslMoreNeeded;// Next // comment is really psl @@ -126,6 +127,7 @@ class V3PreLex { m_fp = fp; m_keepComments = 0; m_pedantic = false; + m_formalLevel = 0; m_parenLevel = 0; m_pslParenLevel = 0; m_pslMoreNeeded = false; diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 291fdb2de..5aea4f207 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -30,18 +30,20 @@ V3PreLex* V3PreLex::s_currentLexp = NULL; // Current lexing point +#define LEXP V3PreLex::s_currentLexp + // Prevent conflicts from perl version -static void linenoInc() {V3PreLex::s_currentLexp->incLineno();} +static void linenoInc() {LEXP->incLineno();} static bool optPsl() { return V3PreProc::optPsl(); } -static bool pedantic() { return V3PreLex::s_currentLexp->m_pedantic; } -static void yyerror(char* msg) { V3PreLex::s_currentLexp->m_curFilelinep->v3error(msg); } -static void yyerrorf(const char* msg) { V3PreLex::s_currentLexp->m_curFilelinep->v3error(msg); } -static void appendDefValue(const char* t,int l) { V3PreLex::s_currentLexp->appendDefValue(t,l); } -static int pslParenLevel() { return V3PreLex::s_currentLexp->m_pslParenLevel; } -static void pslParenLevelInc() { V3PreLex::s_currentLexp->m_pslParenLevel++; } -static void pslParenLevelDec() { if (pslParenLevel()) V3PreLex::s_currentLexp->m_pslParenLevel--; } -static bool pslMoreNeeded() { return V3PreLex::s_currentLexp->m_pslMoreNeeded; } -static void pslMoreNeeded(bool flag) { V3PreLex::s_currentLexp->m_pslMoreNeeded = flag; } +static bool pedantic() { return LEXP->m_pedantic; } +static void yyerror(char* msg) { LEXP->m_curFilelinep->v3error(msg); } +static void yyerrorf(const char* msg) { LEXP->m_curFilelinep->v3error(msg); } +static void appendDefValue(const char* t,int l) { LEXP->appendDefValue(t,l); } +static int pslParenLevel() { return LEXP->m_pslParenLevel; } +static void pslParenLevelInc() { LEXP->m_pslParenLevel++; } +static void pslParenLevelDec() { if (pslParenLevel()) LEXP->m_pslParenLevel--; } +static bool pslMoreNeeded() { return LEXP->m_pslMoreNeeded; } +static void pslMoreNeeded(bool flag) { LEXP->m_pslMoreNeeded = flag; } /**********************************************************************/ %} @@ -75,7 +77,7 @@ psl [p]sl /**************************************************************/ %% -^{ws}*"`line"{ws}+.*{crnl} { V3PreLex::s_currentLexp->lineDirective(yytext); +^{ws}*"`line"{ws}+.*{crnl} { LEXP->lineDirective(yytext); return(VP_LINE); } /* Special directives we recognize */ @@ -92,13 +94,13 @@ psl [p]sl /* Optional directives we recognize */ "`__FILE__" { if (!pedantic()) { static string rtnfile; - rtnfile = '"'; rtnfile += V3PreLex::s_currentLexp->m_curFilelinep->cfilename(); + rtnfile = '"'; rtnfile += LEXP->m_curFilelinep->cfilename(); rtnfile += '"'; yytext=(char*)rtnfile.c_str(); yyleng = rtnfile.length(); return (VP_STRING); } else return(VP_DEFREF); } "`__LINE__" { if (!pedantic()) { static char buf[10]; - sprintf(buf, "%d",V3PreLex::s_currentLexp->m_curFilelinep->lineno()); + sprintf(buf, "%d",LEXP->m_curFilelinep->lineno()); yytext = buf; yyleng = strlen(yytext); return (VP_TEXT); } else return(VP_DEFREF); } "`error" { if (!pedantic()) return (VP_ERROR); else return(VP_DEFREF); } @@ -108,10 +110,10 @@ psl [p]sl <> { linenoInc(); yyerrorf("EOF in unterminated string"); yyleng=0; yyterminate(); } {crnl} { linenoInc(); yyerrorf("Unterminated string"); BEGIN(INITIAL); } [^\"\\] { yymore(); } -{backslash}{crnl} { linenoInc(); yymore(); } +{backslash}{crnl} { linenoInc(); yymore(); } {backslash}. { yymore(); } {quote} { yy_pop_state(); - if (V3PreLex::s_currentLexp->m_parenLevel) appendDefValue(yytext,yyleng); + if (LEXP->m_parenLevel || LEXP->m_formalLevel) { appendDefValue(yytext,yyleng); yyleng=0; } else return (VP_STRING); } /* Protected blocks */ @@ -130,21 +132,24 @@ psl [p]sl /* Reading definition formal parenthesis (or not) to begin formal arguments */ /* Note '(' must IMMEDIATELY follow definition name */ -[(] { appendDefValue("(",1); BEGIN(DEFFORM); } +[(] { appendDefValue("(",1); LEXP->m_formalLevel=1; BEGIN(DEFFORM); } {crnl} { yy_pop_state(); unput('\n'); yyleng=0; return VP_DEFFORM; } /* DEFVAL will later grab the return */ <> { yy_pop_state(); return VP_DEFFORM; } /* empty formals */ . { yy_pop_state(); unput(yytext[yyleng-1]); yyleng=0; return VP_DEFFORM; } /* empty formals */ - + /* Reading definition formals */ -[(] { yy_pop_state(); yyerrorf("Extra ( in define formal arguments.\n"); yyleng=0; return VP_DEFFORM; } -[)] { yy_pop_state(); appendDefValue(yytext,yyleng); yyleng=0; return VP_DEFFORM; } +[(] { appendDefValue(yytext,yyleng); yyleng=0; ++LEXP->m_formalLevel; } +[)] { appendDefValue(yytext,yyleng); yyleng=0; if ((--LEXP->m_formalLevel)==0) { yy_pop_state(); return VP_DEFFORM; } } "/*" { yy_push_state(CMTMODE); yymore(); } "//"[^\n\r]* { return (VP_COMMENT);} -{drop} { } +{drop} { } <> { linenoInc(); yy_pop_state(); yyerrorf("Unterminated ( in define formal arguments."); yyleng=0; return VP_DEFFORM; } {crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */ [\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */ -[^\/\*\n\r\\()]+ | +{quote} { yy_push_state(STRMODE); yymore(); } +[{\[] { LEXP->m_formalLevel++; appendDefValue(yytext,yyleng); } +[}\]] { LEXP->m_formalLevel--; appendDefValue(yytext,yyleng); } +[^\/\*\n\r\\(){}\[\]\"]+ | [\\][^\n\r] | . { appendDefValue(yytext,yyleng); } @@ -166,26 +171,24 @@ psl [p]sl <> { yyerrorf("EOF in define argument list\n"); yyleng = 0; yyterminate(); } {crnl} { linenoInc(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); } {quote} { yy_push_state(STRMODE); yymore(); } -[{\[] { V3PreLex::s_currentLexp->m_parenLevel++; - appendDefValue(yytext,yyleng); } -[}\]] { V3PreLex::s_currentLexp->m_parenLevel--; - appendDefValue(yytext,yyleng); } -[(] { V3PreLex::s_currentLexp->m_parenLevel++; +[{\[] { LEXP->m_parenLevel++; appendDefValue(yytext,yyleng); } +[}\]] { LEXP->m_parenLevel--; appendDefValue(yytext,yyleng); } +[(] { LEXP->m_parenLevel++; // Note paren level 0 means before "(" of starting args // Level 1 means "," between arguments // Level 2+ means one argument's internal () - if (V3PreLex::s_currentLexp->m_parenLevel>1) { + if (LEXP->m_parenLevel>1) { appendDefValue(yytext,yyleng); } else { return (VP_TEXT); }} -[)] { V3PreLex::s_currentLexp->m_parenLevel--; - if (V3PreLex::s_currentLexp->m_parenLevel>0) { +[)] { LEXP->m_parenLevel--; + if (LEXP->m_parenLevel>0) { appendDefValue(yytext,yyleng); } else { yy_pop_state(); return (VP_DEFARG); }} -[,] { if (V3PreLex::s_currentLexp->m_parenLevel>1) { +[,] { if (LEXP->m_parenLevel>1) { appendDefValue(yytext,yyleng); } else { yy_pop_state(); return (VP_DEFARG); diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index f0d6e0f0c..438acd618 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -161,7 +161,7 @@ private: FileLine* defFileline(const string& name); bool commentTokenMatch(string& cmdr, const char* strg); - string trimWhitespace(const string& strg); + string trimWhitespace(const string& strg, bool trailing); void unputString(const string& strg); void parsingOn() { @@ -411,10 +411,19 @@ void V3PreProcImp::unputString(const string& strg) { m_lexp->scanBytes(strg); } -string V3PreProcImp::trimWhitespace(const string& strg) { +string V3PreProcImp::trimWhitespace(const string& strg, bool trailing) { + // Remove leading whitespace string out = strg; - while (out.length()>0 && isspace(out[0])) { - out.erase(0,1); + string::size_type leadspace = 0; + while (out.length() > leadspace + && isspace(out[leadspace])) leadspace++; + if (leadspace) out.erase(0,leadspace); + // Remove trailing whitespace + if (trailing) { + string::size_type trailspace = 0; + while (out.length() > trailspace + && isspace(out[out.length()-1-trailspace])) trailspace++; + if (trailspace) out.erase(out.length()-trailspace,trailspace); } return out; } @@ -429,7 +438,7 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { // parsed result. UINFO(4,"defineSubstIn `"<name()<<" "<params()<args().size(); i++) { - UINFO(4,"defineArg["<args()[i]<name()); @@ -439,27 +448,63 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { { // Parse argument list into map unsigned numArgs=0; string argName; - for (const char* cp=refp->params().c_str(); *cp; cp++) { - if (*cp=='(') { - } else if (argName=="" && isspace(*cp)) { - } else if (isspace(*cp) || *cp==')' || *cp==',') { - if (argName!="") { - if (refp->args().size() > numArgs) { - // A call `def( a ) must be equivelent to `def(a ), so trimWhitespace - // Note other sims don't trim trailing whitespace, so we don't either. - argValueByName[argName] = trimWhitespace(refp->args()[numArgs]); + int paren = 1; // (), {} and [] can use same counter, as must be matched pair per spec + string token; + bool quote = false; + bool haveDefault = false; + // Note there's a leading ( and trailing ), so parens==1 is the base parsing level + const char* cp=refp->params().c_str(); + if (*cp == '(') cp++; + for (; *cp; cp++) { + //UINFO(4," Parse Paren="<args().size() > numArgs) { + // A call `def( a ) must be equivelent to `def(a ), so trimWhitespace + // Note other sims don't trim trailing whitespace, so we don't either. + string arg = trimWhitespace(refp->args()[numArgs], false); + if (arg != "") value = arg; + } else if (!haveDefault) { + fileline()->v3error("Define missing argument '"+argName+"' for: "+refp->name()+"\n"); + return " `"+refp->name()+" "; + } + numArgs++; } - numArgs++; - //cout << " arg "<args().size() != numArgs) { - fileline()->v3error("Define passed wrong number of arguments: "+refp->name()+"\n"); + if (refp->args().size() > numArgs) { + fileline()->v3error("Define passed too many arguments: "+refp->name()+"\n"); return " `"+refp->name()+" "; } } @@ -768,42 +813,27 @@ int V3PreProcImp::getToken() { if (tok == VP_DEFVALUE) { if (debug()) cout<<"DefValue='"<m_defValue<<"' formals='"<m_defValue; + string formals = m_formals; + string value = m_lexp->m_defValue; // Remove returns - for (unsigned i=0; iv3error("Missing ) to end define arguments."); - } else { - params = formAndValue.substr(0, paren+1); - formAndValue.replace(0, paren+1, ""); - } - } else { - fileline()->v3error("Missing space or paren to start define value."); - } - // Remove leading whitespace - unsigned leadspace = 0; - while (formAndValue.length() > leadspace - && isspace(formAndValue[leadspace])) leadspace++; - if (leadspace) formAndValue.erase(0,leadspace); - // Remove trailing whitespace - unsigned trailspace = 0; - while (formAndValue.length() > trailspace - && isspace(formAndValue[formAndValue.length()-1-trailspace])) trailspace++; - if (trailspace) formAndValue.erase(formAndValue.length()-trailspace,trailspace); + // Remove leading and trailing whitespace + value = trimWhitespace(value, true); // Define it - UINFO(4,"Define "<v3fatalSrc("Bad define text\n"); diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 64983582c..39ee72619 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -131,7 +131,7 @@ private: outrefp = outp->castSel()->fromp()->castVarRef(); width = outp->castSel()->widthConst(); } else { - v3error("Can't find LHS varref"); + nodep->v3error("Can't find LHS varref"); } outrefp->lvalue(true); AstVar* varp = outrefp->varp(); @@ -185,7 +185,7 @@ private: } else if (outp->castSel()) { outrefp = outp->castSel()->fromp()->castVarRef(); } else { - v3error("Can't find LHS varref"); + nodep->v3error("Can't find LHS varref"); } createEnableVar(outp, outrefp, enrhsp, outrhsp->width()); @@ -215,7 +215,7 @@ private: enrhsp = new AstReplicate(enrhsp->fileline(), enrhsp, new AstConst(enrhsp->fileline(), V3Number(enrhsp->fileline(), 32, enp->width()))); enrhsp->width(enp->width(), enp->widthMin()); } else { - v3error("Don't know how to deal with selection logic wider than 1 bit"); + enrhsp->v3error("Don't know how to deal with selection logic wider than 1 bit"); } } m_modp->addStmtp(enp); @@ -351,7 +351,7 @@ private: // not sure what I should do here other than error that they are mixing low-Z and tristate drivers. // The other scenerio, and probably more likely, is that they are using a high-Z construct that // is not supported. Improving the high-Z detection logic will reduce the occurance of this failure. - v3error("Mixing tristate and low-Z drivers. Perhaps you are using a high-Z construct not supported"); + nodep->v3error("Mixing tristate and low-Z drivers. Perhaps you are using a high-Z construct not supported"); } else { UINFO(9, " No tristates found on " << lhsp <{v3}) { + ok(1); +} else { + compile ( + v_flags2 => ['-E'], + verilator_make_gcc=>0, + stdout_filename => $stdout_filename, + ); + ok(files_identical($stdout_filename, "t/$Self->{name}.out")); +} + +1; diff --git a/test_regress/t/t_preproc_def09.v b/test_regress/t/t_preproc_def09.v new file mode 100644 index 000000000..88c27c235 --- /dev/null +++ b/test_regress/t/t_preproc_def09.v @@ -0,0 +1,73 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2009 by Wilson Snyder. + +`undefineall + +// Definitions as speced +// Note there are trailing spaces, which spec doesn't show properly +`define D(x,y) initial $display("start", x , y, "end"); +'`D( "msg1" , "msg2" )' +'initial $display("start", "msg1" , "msg2" , "end");' +'`D( " msg1", )' +'initial $display("start", " msg1" , , "end");' +'`D(, "msg2 ")' +'initial $display("start", , "msg2 ", "end");' +'`D(,)' +'initial $display("start", , , "end");' +'`D( , )' +'initial $display("start", , , "end");' +//`D("msg1") // ILLEGAL: only one argument +//`D() // ILLEGAL: only one empty argument +//`D(,,) // ILLEGAL: more actual than formal arguments + +// Defaults: +`define MACRO1(a=5,b="B",c) $display(a,,b,,c); +'`MACRO1 ( , 2, 3 )' +'$display(5,,2,,3);' +'`MACRO1 ( 1 , , 3 )' +'$display(1 ,,"B",,3 );' +'`MACRO1 ( , 2, )' +'$display(5,,2,,);' +//`MACRO1 ( 1 ) // ILLEGAL: b and c omitted, no default for c + +`define MACRO2(a=5, b, c="C") $display(a,,b,,c); +'`MACRO2 (1, , 3)' +'$display(5,,,,"C");' +'`MACRO2 (, 2, )' +'$display(5,,2,,"C");' +'`MACRO2 (, 2)' +'$display(5,,2,,"C");' + +`define MACRO3(a=5, b=0, c="C") $display(a,,b,,c); +'`MACRO3 ( 1 )' +'$display(1 ,,0,,"C");' +'`MACRO3 ( )' +'$display(5,,0,,"C");' +//`MACRO3 // ILLEGAL: parentheses required + +`define DTOP(a,b) a + b +'`DTOP( `DTOP(b,1), `DTOP(42,a) )' +'b + 1 + 42 + a' + +// Local tests +`define MACROQUOTE(a="==)",b="((((",c=() ) 'a b c' +`MACROQUOTE(); +'"==)" "((((" () '; + +// Also check our line counting doesn't go bad +`define MACROPAREN(a=(6), + b=(eq=al), + c) 'a b c' +`MACROPAREN( + + + + ,, + + + ZOT) +HERE-`__LINE__ - Line71 + +//======================================================================