pythonsoliditygrammarpyparsing

Infinite recursion in pyparsing grammar for method signatures


Below is my pyparsing grammar for parsing the method signature of a Solidity function, and an example signature to parse:

from pyparsing import Word, alphas, alphanums, oneOf, Group, Forward, ZeroOrMore, Optional, delimitedList

solidity_type = Forward()
primitive = oneOf("address uint uint8 uint16 uint32 uint64 uint128 uint256 int int8 int16 int32 int64 int128 int256 bool bytes bytes1 bytes4 bytes32")
array = solidity_type + "[]"
tuple_type = "(" + delimitedList(solidity_type) + ")"
solidity_type <<= (primitive | array | tuple_type)
function_name = Word(alphas, alphanums + "_")
arguments = Optional(delimitedList(solidity_type))
function_signature = function_name + "(" + arguments + ")"

sig = "diamondCut((address,uint8,bytes4[])[],address,bytes)"
parsed_sig = function_signature.parseString(sig)
print(parsed_sig.dump())

The error I get is {RecursionError}maximum recursion depth exceeded. Narrowing it down, I realize that this statement produces the same error:

array.parseString("(bytes4)[]")

I tried surrounding some of the expressions with Group, and re-ordering the options in the solidity_type definition. I also tried defining array as its own Forward.

I don't understand how to change my code so that the example signature parses without error.


Solution

  • Your grammar is left recursive.

    During the recursion, no tokens are consumed. That is why you get the recursion error.

    Fortunately, pyparsing can handle that, but you have to enable that feature:

    from pyparsing import ParserElement
    
    ParserElement.enable_left_recursion()
    # rest of your grammar here
    

    You should also rewrite the solidity_type by swapping array and primitive. array should come first, because it is more specific.