diff options
Diffstat (limited to 'examples/verilog_parse.py')
-rw-r--r-- | examples/verilog_parse.py | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/examples/verilog_parse.py b/examples/verilog_parse.py new file mode 100644 index 0000000..6a80811 --- /dev/null +++ b/examples/verilog_parse.py @@ -0,0 +1,946 @@ +# +# verilogParse.py +# +# an example of using the pyparsing module to be able to process Verilog files +# uses BNF defined at http://www.verilog.com/VerilogBNF.html +# +# Copyright (c) 2004-2011 Paul T. McGuire. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# If you find this software to be useful, please make a donation to one +# of the following charities: +# - the Red Cross (https://www.redcross.org/) +# - Hospice Austin (https://www.hospiceaustin.org/) +# +# DISCLAIMER: +# THIS SOFTWARE IS PROVIDED BY PAUL T. McGUIRE ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL PAUL T. McGUIRE OR CO-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OFUSE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# For questions or inquiries regarding this license, or commercial use of +# this software, contact the author via e-mail: ptmcg@users.sourceforge.net +# +# Todo: +# - add pre-process pass to implement compilerDirectives (ifdef, include, etc.) +# +# Revision History: +# +# 1.0 - Initial release +# 1.0.1 - Fixed grammar errors: +# . real declaration was incorrect +# . tolerant of '=>' for '*>' operator +# . tolerant of '?' as hex character +# . proper handling of mintypmax_expr within path delays +# 1.0.2 - Performance tuning (requires pyparsing 1.3) +# 1.0.3 - Performance updates, using Regex (requires pyparsing 1.4) +# 1.0.4 - Performance updates, enable packrat parsing (requires pyparsing 1.4.2) +# 1.0.5 - Converted keyword Literals to Keywords, added more use of Group to +# group parsed results tokens +# 1.0.6 - Added support for module header with no ports list (thanks, Thomas Dejanovic!) +# 1.0.7 - Fixed erroneous '<<' Forward definition in timCheckCond, omitting ()'s +# 1.0.8 - Re-released under MIT license +# 1.0.9 - Enhanced udpInstance to handle identifiers with leading '\' and subscripting +# 1.0.10 - Fixed change added in 1.0.9 to work for all identifiers, not just those used +# for udpInstance. +# 1.0.11 - Fixed bug in inst_args, content alternatives were reversed +# 1.1.0 - Some performance fixes, convert most literal strs to Keywords +# +from pathlib import Path +import pprint +import time + +__version__ = "1.1.0" + +__all__ = ["__version__", "verilogbnf"] + +from pyparsing import ( + Literal, + Keyword, + Word, + Forward, + DelimitedList, + Group, + Optional, + Combine, + alphas, + nums, + restOfLine, + alphanums, + dbl_quoted_string, + empty, + ParseException, + one_of, + StringEnd, + FollowedBy, + ParserElement, + Regex, + cppStyleComment, +) +import pyparsing + +usePackrat = True + +packratOn = False +if usePackrat: + try: + ParserElement.enable_packrat() + except Exception: + pass + else: + packratOn = True + + +verilogbnf = None + + +def make_verilog_bnf(): + global verilogbnf + + if verilogbnf is None: + + # compiler directives + compilerDirective = Combine( + "`" + + one_of( + "define undef ifdef else endif default_nettype" + " include resetall timescale unconnected_drive" + " nounconnected_drive celldefine endcelldefine", + as_keyword=True, + ) + + restOfLine + ).set_name("compilerDirective") + + # primitives + ( + SEMI, + COLON, + LPAR, + RPAR, + LBRACE, + RBRACE, + LBRACK, + RBRACK, + DOT, + COMMA, + EQ, + ) = Literal.using_each(";:(){}[].,=") + + identLead = alphas + "$_" + identBody = alphanums + "$_" + identifier1 = Regex( + rf"\.?[{identLead}][{identBody}]*(\.[{identLead}][{identBody}]*)*" + ).set_name("baseIdent") + identifier2 = ( + Regex(r"\\\S+").setParseAction(lambda t: t[0][1:]).set_name("escapedIdent") + ) # .setDebug() + identifier = identifier1 | identifier2 + assert identifier2 == r"\abc" + + hexnums = nums + "abcdefABCDEF" + "_?" + base = Regex("'[bBoOdDhH]").set_name("base") + basedNumber = Combine( + (Word(nums + "_") | "") + base + Word(hexnums + "xXzZ"), + joinString=" ", + adjacent=False, + ).set_name("basedNumber") + # number = ( basedNumber | Combine( Word( "+-"+spacedNums, spacedNums ) + + # Optional( DOT + Optional( Word( spacedNums ) ) ) + + # Optional( e + Word( "+-"+spacedNums, spacedNums ) ) ).set_name("numeric") ) + number = ( + basedNumber | Regex(r"[+-]?[0-9_]+(\.[0-9_]*)?([Ee][+-]?[0-9_]+)?") + ).set_name("numeric") + + expr = Forward().set_name("expr") + concat = Group(LBRACE + DelimitedList(expr) + RBRACE) + multiConcat = Group("{" + expr + concat + "}").set_name("multiConcat") + funcCall = Group( + identifier + LPAR + (DelimitedList(expr) | "") + RPAR + ).set_name("funcCall") + + subscrRef = Group(LBRACK + DelimitedList(expr, COLON) + RBRACK) + subscrIdentifier = Group(identifier + (subscrRef | "")) + # scalarConst = "0" | (( FollowedBy('1') + one_of("1'b0 1'b1 1'bx 1'bX 1'B0 1'B1 1'Bx 1'BX 1") )) + scalarConst = Regex("0|1('[Bb][01xX])?") + mintypmaxExpr = Group(expr + COLON + expr + COLON + expr).set_name("mintypmax") + primary = ( + number + | (LPAR + mintypmaxExpr + RPAR) + | (LPAR + Group(expr) + RPAR).set_name("nestedExpr") + | multiConcat + | concat + | dbl_quoted_string + | funcCall + | subscrIdentifier + ) + + unop = one_of("+ - ! ~ & ~& | ^| ^ ~^").set_name("unop") + binop = one_of( + "+ - * / % == != === !== && " + "|| < <= > >= & | ^ ^~ >> << ** <<< >>>" + ).set_name("binop") + + expr <<= ( + (unop + expr) + | (primary + "?" + expr + COLON + expr) # must be first! + | (primary + ((binop + expr) | "")) + ) + + lvalue = subscrIdentifier | concat + + # keywords + reg = Keyword("reg") + trireg = Keyword("trireg") + signed = Keyword("signed") + parameter = Keyword("parameter") + input_, output, inout = Keyword.using_each("input output inout".split()) + time = Keyword("time") + integer = Keyword("integer") + real = Keyword("real") + event = Keyword("event") + scalared = Keyword("scalared") + vectored = Keyword("vectored") + if_ = Keyword("if") + else_ = Keyword("else") + always = Keyword("always") + initial = Keyword("initial") + small, medium, large = Keyword.using_each("small medium large".split()) + edge = Keyword("edge") + posedge = Keyword("posedge") + negedge = Keyword("negedge") + specify, endspecify = Keyword.using_each("specify endspecify".split()) + primitive, endprimitive = Keyword.using_each("primitive endprimitive".split()) + fork = Keyword("fork") + join = Keyword("join") + begin = Keyword("begin") + end = Keyword("end") + default = Keyword("default") + forever = Keyword("forever") + repeat = Keyword("repeat") + while_ = Keyword("while") + for_ = Keyword("for") + case = one_of("case casez casex", as_keyword=True) + endcase = Keyword("endcase") + wait = Keyword("wait") + disable = Keyword("disable") + deassign = Keyword("deassign") + force = Keyword("force") + release = Keyword("release") + assign = Keyword("assign") + table, endtable = Keyword.using_each("table endtable".split()) + function, endfunction = Keyword.using_each("function endfunction".split()) + task, endtask = Keyword.using_each("task endtask".split()) + module, macromodule, endmodule = Keyword.using_each( + "module macromodule endmodule".split() + ) + + eventExpr = Forward() + eventTerm = ( + (posedge + expr) | (negedge + expr) | expr | (LPAR + eventExpr + RPAR) + ) + eventExpr <<= Group(DelimitedList(eventTerm, Keyword("or"))) + eventControl = Group( + "@" + ((LPAR + eventExpr + RPAR) | identifier | "*") + ).set_name("eventCtrl") + + delayArg = ( + number + | Word(alphanums + "$_") + | (LPAR + Group(DelimitedList(mintypmaxExpr | expr)) + RPAR) # identifier | + ).set_name( + "delayArg" + ) # .setDebug() + delay = Group("#" + delayArg).set_name("delay") # .setDebug() + delayOrEventControl = delay | eventControl + + assgnmt = Group(lvalue + EQ + (delayOrEventControl | "") + expr).set_name( + "assgnmt" + ) + nbAssgnmt = Group( + (lvalue + "<=" + (delay | "") + expr) + | (lvalue + "<=" + (eventControl | "") + expr) + ).set_name("nbassgnmt") + + range_ = LBRACK + expr + COLON + expr + RBRACK + + paramAssgnmt = Group(identifier + EQ + expr).set_name("paramAssgnmt") + parameterDecl = Group( + parameter + (range_ | "") + DelimitedList(paramAssgnmt) + SEMI + ).set_name("paramDecl") + + inputDecl = Group(input_ + (range_ | "") + DelimitedList(identifier) + SEMI) + outputDecl = Group(output + (range_ | "") + DelimitedList(identifier) + SEMI) + inoutDecl = Group(inout + (range_ | "") + DelimitedList(identifier) + SEMI) + + regIdentifier = Group(identifier + (LBRACK + expr + COLON + expr + RBRACK | "")) + regDecl = Group( + reg + (signed | "") + (range_ | "") + DelimitedList(regIdentifier) + SEMI + ).set_name("regDecl") + timeDecl = Group(time + DelimitedList(regIdentifier) + SEMI) + integerDecl = Group(integer + DelimitedList(regIdentifier) + SEMI) + + strength0 = one_of("supply0 strong0 pull0 weak0 highz0", as_keyword=True) + strength1 = one_of("supply1 strong1 pull1 weak1 highz1", as_keyword=True) + driveStrength = Group( + LPAR + + ((strength0 + COMMA + strength1) | (strength1 + COMMA + strength0)) + + RPAR + ).set_name("driveStrength") + nettype = one_of( + "wire tri tri1 supply0 wand triand tri0 supply1 wor trior trireg", + as_keyword=True, + ) + expandRange = (scalared | vectored | "") + range_ + realDecl = Group(real + DelimitedList(identifier) + SEMI) + + eventDecl = Group(event + DelimitedList(identifier) + SEMI) + + blockDecl = ( + parameterDecl | regDecl | integerDecl | realDecl | timeDecl | eventDecl + ) + + stmt = Forward().set_name("stmt") # .setDebug() + stmtOrNull = stmt | SEMI + caseItem = (DelimitedList(expr) + COLON + stmtOrNull) | ( + default + Optional(":") + stmtOrNull + ) + stmt <<= Group( + (begin + Group(stmt[...:end]) + end).set_name("begin-end") + | ( + if_ + Group(LPAR + expr + RPAR) + stmtOrNull + (else_ + stmtOrNull | "") + ).set_name("if") + | (delayOrEventControl + stmtOrNull) + | (case + LPAR + expr + RPAR + caseItem[1, ...] + endcase) + | (forever + stmt) + | (repeat + LPAR + expr + RPAR + stmt) + | (while_ + LPAR + expr + RPAR + stmt) + | ( + for_ + + LPAR + + assgnmt + + SEMI + + Group(expr) + + SEMI + + assgnmt + + RPAR + + stmt + ) + | (fork + stmt[...] + join) + | (fork + COLON + identifier + blockDecl[...] + stmt[...] + end) + | (wait + LPAR + expr + RPAR + stmtOrNull) + | ("->" + identifier + SEMI) + | (disable + identifier + SEMI) + | (assign + assgnmt + SEMI) + | (deassign + lvalue + SEMI) + | (force + assgnmt + SEMI) + | (release + lvalue + SEMI) + | (begin + COLON + identifier + blockDecl[...] + stmt[...] + end).set_name( + "begin:label-end" + ) + | + # these *have* to go at the end of the list!!! + (assgnmt + SEMI) + | (nbAssgnmt + SEMI) + | ( + Combine(Optional("$") + identifier) + + (LPAR + DelimitedList(expr | empty) + RPAR | "") + + SEMI + ) + ).set_name("stmtBody") + """ + x::=<blocking_assignment> ; + x||= <non_blocking_assignment> ; + x||= if ( <expression> ) <statement_or_null> + x||= if ( <expression> ) <statement_or_null> else <statement_or_null> + x||= case ( <expression> ) <case_item>+ endcase + x||= casez ( <expression> ) <case_item>+ endcase + x||= casex ( <expression> ) <case_item>+ endcase + x||= forever <statement> + x||= repeat ( <expression> ) <statement> + x||= while ( <expression> ) <statement> + x||= for ( <assignment> ; <expression> ; <assignment> ) <statement> + x||= <delay_or_event_control> <statement_or_null> + x||= wait ( <expression> ) <statement_or_null> + x||= -> <name_of_event> ; + x||= <seq_block> + x||= <par_block> + x||= <task_enable> + x||= <system_task_enable> + x||= disable <name_of_task> ; + x||= disable <name_of_block> ; + x||= assign <assignment> ; + x||= deassign <lvalue> ; + x||= force <assignment> ; + x||= release <lvalue> ; + """ + alwaysStmt = Group(always + (eventControl | "") + stmt).set_name("alwaysStmt") + initialStmt = Group(initial + stmt).set_name("initialStmt") + + chargeStrength = Group(LPAR + (small | medium | large) + RPAR).set_name( + "chargeStrength" + ) + + continuousAssign = Group( + assign + (driveStrength | "") + (delay | "") + DelimitedList(assgnmt) + SEMI + ).set_name("continuousAssign") + + tfDecl = ( + parameterDecl + | inputDecl + | outputDecl + | inoutDecl + | regDecl + | timeDecl + | integerDecl + | realDecl + ) + + functionDecl = Group( + function + + (range_ | "integer" | "real" | "") + + identifier + + SEMI + + Group(tfDecl[1, ...]) + + Group(stmt[...]) + + endfunction + ) + + inputOutput = input_ | output + netDecl1Arg = ( + nettype + + (expandRange | "") + + (delay | "") + + Group(DelimitedList(~inputOutput + identifier)) + ) + netDecl2Arg = ( + trireg + + (chargeStrength | "") + + (expandRange | "") + + (delay | "") + + Group(DelimitedList(~inputOutput + identifier)) + ) + netDecl3Arg = ( + nettype + + (driveStrength | "") + + (expandRange | "") + + (delay | "") + + Group(DelimitedList(assgnmt)) + ) + netDecl1 = Group(netDecl1Arg + SEMI).set_name("netDecl1") + netDecl2 = Group(netDecl2Arg + SEMI).set_name("netDecl2") + netDecl3 = Group(netDecl3Arg + SEMI).set_name("netDecl3") + + gateType = one_of( + "and nand or nor xor xnor buf bufif0 bufif1 " + "not notif0 notif1 pulldown pullup nmos rnmos " + "pmos rpmos cmos rcmos tran rtran tranif0 " + "rtranif0 tranif1 rtranif1", + as_keyword=True, + ) + gateInstance = ( + (Group(identifier + (range_ | "")) | "") + + LPAR + + Group(DelimitedList(expr)) + + RPAR + ) + gateDecl = Group( + gateType + + (driveStrength | "") + + (delay | "") + + DelimitedList(gateInstance) + + SEMI + ) + + udpInstance = Group( + Group(identifier + (range_ | subscrRef | "")) + + LPAR + + Group(DelimitedList(expr)) + + RPAR + ) + udpInstantiation = Group( + identifier + - (driveStrength | "") + + (delay | "") + + DelimitedList(udpInstance) + + SEMI + ).set_name("udpInstantiation") + + parameterValueAssignment = Group( + Literal("#") + LPAR + Group(DelimitedList(expr)) + RPAR + ) + namedPortConnection = Group(DOT + identifier + LPAR + expr + RPAR).set_name( + "namedPortConnection" + ) # .setDebug() + # assert r".\abc (abc )" == namedPortConnection + modulePortConnection = expr | empty + inst_args = Group( + LPAR + + (DelimitedList(namedPortConnection) | DelimitedList(modulePortConnection)) + + RPAR + ).set_name("inst_args") + moduleInstance = Group(Group(identifier + (range_ | "")) + inst_args).set_name( + "moduleInstance" + ) # .setDebug() + + moduleInstantiation = Group( + identifier + + (parameterValueAssignment | "") + + DelimitedList(moduleInstance).set_name("moduleInstanceList") + + SEMI + ).set_name("moduleInstantiation") + + parameterOverride = Group("defparam" + DelimitedList(paramAssgnmt) + SEMI) + task = Group(task + identifier + SEMI + tfDecl[...] + stmtOrNull + endtask) + + specparamDecl = Group("specparam" + DelimitedList(paramAssgnmt) + SEMI) + + pathDescr1 = Group(LPAR + subscrIdentifier + "=>" + subscrIdentifier + RPAR) + pathDescr2 = Group( + LPAR + + Group(DelimitedList(subscrIdentifier)) + + "*>" + + Group(DelimitedList(subscrIdentifier)) + + RPAR + ) + pathDescr3 = Group( + LPAR + + Group(DelimitedList(subscrIdentifier)) + + "=>" + + Group(DelimitedList(subscrIdentifier)) + + RPAR + ) + pathDelayValue = Group( + (LPAR + Group(DelimitedList(mintypmaxExpr | expr)) + RPAR) + | mintypmaxExpr + | expr + ) + pathDecl = Group( + (pathDescr1 | pathDescr2 | pathDescr3) + EQ + pathDelayValue + SEMI + ).set_name("pathDecl") + + portConditionExpr = Forward() + portConditionTerm = (unop | "") + subscrIdentifier + portConditionExpr <<= portConditionTerm + (binop + portConditionExpr | "") + polarityOp = one_of("+ -") + levelSensitivePathDecl1 = Group( + if_ + + Group(LPAR + portConditionExpr + RPAR) + + subscrIdentifier + + (polarityOp | "") + + "=>" + + subscrIdentifier + + EQ + + pathDelayValue + + SEMI + ) + levelSensitivePathDecl2 = Group( + if_ + + Group(LPAR + portConditionExpr + RPAR) + + LPAR + + Group(DelimitedList(subscrIdentifier)) + + (polarityOp | "") + + "*>" + + Group(DelimitedList(subscrIdentifier)) + + RPAR + + EQ + + pathDelayValue + + SEMI + ) + levelSensitivePathDecl = levelSensitivePathDecl1 | levelSensitivePathDecl2 + + edgeIdentifier = posedge | negedge + edgeSensitivePathDecl1 = Group( + (if_ + Group(LPAR + expr + RPAR) | "") + + LPAR + + (edgeIdentifier | "") + + subscrIdentifier + + "=>" + + LPAR + + subscrIdentifier + + (polarityOp | "") + + COLON + + expr + + RPAR + + RPAR + + EQ + + pathDelayValue + + SEMI + ) + edgeSensitivePathDecl2 = Group( + (if_ + Group(LPAR + expr + RPAR) | "") + + LPAR + + (edgeIdentifier | "") + + subscrIdentifier + + "*>" + + LPAR + + DelimitedList(subscrIdentifier) + + (polarityOp | "") + + COLON + + expr + + RPAR + + RPAR + + EQ + + pathDelayValue + + SEMI + ) + edgeSensitivePathDecl = edgeSensitivePathDecl1 | edgeSensitivePathDecl2 + + edgeDescr = one_of("01 10 0x x1 1x x0").set_name("edgeDescr") + + timCheckEventControl = Group( + posedge | negedge | (edge + LBRACK + DelimitedList(edgeDescr) + RBRACK) + ) + timCheckCond = Forward() + timCondBinop = one_of("== === != !==") + timCheckCondTerm = (expr + timCondBinop + scalarConst) | (Optional("~") + expr) + timCheckCond <<= (LPAR + timCheckCond + RPAR) | timCheckCondTerm + timCheckEvent = Group( + (timCheckEventControl | "") + subscrIdentifier + ("&&&" + timCheckCond | "") + ) + timCheckLimit = expr + controlledTimingCheckEvent = Group( + timCheckEventControl + subscrIdentifier + ("&&&" + timCheckCond | "") + ) + notifyRegister = identifier + + systemTimingCheck1 = Group( + "$setup" + + LPAR + + timCheckEvent + + COMMA + + timCheckEvent + + COMMA + + timCheckLimit + + (COMMA + notifyRegister | "") + + RPAR + + SEMI + ) + systemTimingCheck2 = Group( + "$hold" + + LPAR + + timCheckEvent + + COMMA + + timCheckEvent + + COMMA + + timCheckLimit + + (COMMA + notifyRegister | "") + + RPAR + + SEMI + ) + systemTimingCheck3 = Group( + "$period" + + LPAR + + controlledTimingCheckEvent + + COMMA + + timCheckLimit + + (COMMA + notifyRegister | "") + + RPAR + + SEMI + ) + systemTimingCheck4 = Group( + "$width" + + LPAR + + controlledTimingCheckEvent + + COMMA + + timCheckLimit + + (COMMA + expr + COMMA + notifyRegister | "") + + RPAR + + SEMI + ) + systemTimingCheck5 = Group( + "$skew" + + LPAR + + timCheckEvent + + COMMA + + timCheckEvent + + COMMA + + timCheckLimit + + (COMMA + notifyRegister | "") + + RPAR + + SEMI + ) + systemTimingCheck6 = Group( + "$recovery" + + LPAR + + controlledTimingCheckEvent + + COMMA + + timCheckEvent + + COMMA + + timCheckLimit + + (COMMA + notifyRegister | "") + + RPAR + + SEMI + ) + systemTimingCheck7 = Group( + "$setuphold" + + LPAR + + timCheckEvent + + COMMA + + timCheckEvent + + COMMA + + timCheckLimit + + COMMA + + timCheckLimit + + (COMMA + notifyRegister | "") + + RPAR + + SEMI + ) + systemTimingCheck = ( + FollowedBy("$") + + ( + systemTimingCheck1 + | systemTimingCheck2 + | systemTimingCheck3 + | systemTimingCheck4 + | systemTimingCheck5 + | systemTimingCheck6 + | systemTimingCheck7 + ) + ).set_name("systemTimingCheck") + sdpd = ( + if_ + + Group(LPAR + expr + RPAR) + + (pathDescr1 | pathDescr2) + + EQ + + pathDelayValue + + SEMI + ) + + specifyItem = ( + specparamDecl + | pathDecl + | levelSensitivePathDecl + | edgeSensitivePathDecl + | systemTimingCheck + | sdpd + ) + """ + x::= <specparam_declaration> + x||= <path_declaration> + x||= <level_sensitive_path_declaration> + x||= <edge_sensitive_path_declaration> + x||= <system_timing_check> + x||= <sdpd> + """ + specifyBlock = Group( + specify + specifyItem[...:endspecify] + endspecify + ).set_name("specifyBlock") + + moduleItem = ( + parameterDecl + | inputDecl + | outputDecl + | inoutDecl + | regDecl + | netDecl3 + | netDecl1 + | netDecl2 + | timeDecl + | integerDecl + | realDecl + | eventDecl + | gateDecl + | parameterOverride + | continuousAssign + | specifyBlock + | initialStmt + | alwaysStmt + | task + | functionDecl + # these have to be at the end - they start with identifiers + | moduleInstantiation + | udpInstantiation + ) + """ All possible moduleItems, from Verilog grammar spec + x::= <parameter_declaration> + x||= <input_declaration> + x||= <output_declaration> + x||= <inout_declaration> + ?||= <net_declaration> (spec does not seem consistent for this item) + x||= <reg_declaration> + x||= <time_declaration> + x||= <integer_declaration> + x||= <real_declaration> + x||= <event_declaration> + x||= <gate_declaration> + x||= <UDP_instantiation> + x||= <module_instantiation> + x||= <parameter_override> + x||= <continuous_assign> + x||= <specify_block> + x||= <initial_statement> + x||= <always_statement> + x||= <task> + x||= <function> + """ + portRef = subscrIdentifier + portExpr = portRef | Group(LBRACE + DelimitedList(portRef) + RBRACE) + port = portExpr | Group(DOT + identifier + LPAR + portExpr + RPAR) + + moduleHdr = Group( + (module | macromodule) + + identifier + + ( + LPAR + + Group( + ( + DelimitedList( + Group( + (input_ | output) + + (netDecl1Arg | netDecl2Arg | netDecl3Arg) + ) + | port + ) + | "" + ) + ) + + RPAR + | "" + ) + + SEMI + ).set_name("moduleHdr") + + module_expr = Group( + moduleHdr + Group(moduleItem[...:endmodule]) + endmodule + ).set_name( + "module" + ) # .setDebug() + + udpDecl = outputDecl | inputDecl | regDecl + # udpInitVal = one_of("1'b0 1'b1 1'bx 1'bX 1'B0 1'B1 1'Bx 1'BX 1 0 x X") + udpInitVal = (Regex("1'[bB][01xX]|[01xX]")).set_name("udpInitVal") + udpInitialStmt = Group( + "initial" + identifier + EQ + udpInitVal + SEMI + ).set_name("udpInitialStmt") + + levelSymbol = one_of("0 1 x X ? b B") + levelInputList = Group(levelSymbol[1, ...].set_name("levelInpList")) + + outputSymbol = one_of("0 1 x X") + combEntry = Group(levelInputList + COLON + outputSymbol + SEMI) + edgeSymbol = one_of("r R f F p P n N *") + edge = Group(LPAR + levelSymbol + levelSymbol + RPAR) | Group(edgeSymbol) + edgeInputList = Group(levelSymbol[...] + edge + levelSymbol[...]) + inputList = levelInputList | edgeInputList + seqEntry = Group( + inputList + COLON + levelSymbol + COLON + (outputSymbol | "-") + SEMI + ).set_name("seqEntry") + udpTableDefn = Group( + table + (combEntry | seqEntry)[1, ...] + endtable + ).set_name("table") + + """ + <UDP> + ::= primitive <name_of_UDP> ( <name_of_variable> <,<name_of_variable>>* ) ; + <UDP_declaration>+ + <UDP_initial_statement>? + <table_definition> + endprimitive + """ + udp = Group( + primitive + + identifier + + LPAR + + Group(DelimitedList(identifier)) + + RPAR + + SEMI + + udpDecl[1, ...] + + (udpInitialStmt | "") + + udpTableDefn + + endprimitive + ) + + verilogbnf = (module_expr | udp)[1, ...] + StringEnd() + + verilogbnf.ignore(cppStyleComment) + verilogbnf.ignore(compilerDirective) + + return verilogbnf + + +def test(strng): + tokens = [] + try: + tokens = make_verilog_bnf().parse_string(strng) + except ParseException as err: + print(err.explain()) + return tokens + + +if __name__ == "__main__": + + def main(): + import sys + + sys.setrecursionlimit(5000) + print(f"Verilog parser test (V {__version__})") + print(f" - using pyparsing version {pyparsing.__version__}") + print(f" - using Python version {sys.version}") + if packratOn: + print(" - using packrat parsing") + print() + + import gc + + failCount = 0 + make_verilog_bnf() + numlines = 0 + fileDir = "verilog" + fileDir = "scratch/verilog" + # fileDir = "scratch/verilog/new3" + fileDir = Path(fileDir) + allFiles = [f for f in fileDir.glob("*.v")] + + pretty = pprint.PrettyPrinter(indent=2) + totalTime = 0 + for vfile in allFiles: + gc.collect() + gc.collect() + filelines = vfile.read_text().splitlines() + print(vfile.name, len(filelines), end=" ") + numlines += len(filelines) + teststr = "\n".join(filelines) + time1 = time.perf_counter() + tokens = test(teststr) + time2 = time.perf_counter() + elapsed = time2 - time1 + totalTime += elapsed + if len(tokens): + print(f"OK {elapsed}") + + (fileDir / "parseOutput").mkdir(exist_ok=True) + outfile = fileDir / "parseOutput" / (vfile.name + ".parsed.txt") + outfile.write_text(f"{teststr}\n\n{pretty.pformat(tokens.as_list())}\n") + else: + print(f"failed {elapsed}") + failCount += 1 + for i, line in enumerate(filelines, 1): + print(f"{i:4d}: {line.rstrip()}") + + print(f"Total parse time: {totalTime}") + print(f"Total source lines: {numlines}") + print(f"Average lines/sec: {numlines / (totalTime + 0.05):.1f}") + if failCount: + print(f"FAIL - {failCount} files failed to parse") + else: + print("SUCCESS - all files parsed") + + return 0 + + main() |