pyparsing 解析 c/cpp 枚举值作为用户定义的宏

pyparsing parse c/cpp enums with values as user defined macros

我有一个用例,我需要匹配值可以是用户定义的宏的枚举。

枚举示例

typedef enum
{
  VAL_1 = -1
  VAL_2 =  0,
  VAL_3 = 0x10,
  VAL_4 = **TEST_ENUM_CUSTOM(1,2)**,
}MyENUM;

我正在使用下面的代码,如果我不使用 VAL_4 中的格式,它就可以工作。我也需要 VAL_4 中的匹配格式。我是 pyparsing 的新手,欢迎任何帮助。

我的代码:

BRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,")

_enum = Suppress("enum")
identifier = Word(alphas, alphanums + "_")
integer = Word("-"+alphanums) **#I have tried to "_(,)" to this but is not matching.**

enumValue = Group(identifier("name") + Optional(EQ + integer("value")))
enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue) + Optional(COMMA))
enum = _enum + Optional(identifier("enum")) + LBRACE + enumList("names") + RBRACE + Optional(identifier("typedef"))

enum.ignore(cppStyleComment)
enum.ignore(cStyleComment)

谢谢

-普纳

只是向 integer 添加更多字符是错误的做法。即使是这个表达式:

integer = Word("-"+alphanums)

不是特别好,因为它会匹配“---”、“xyz”、“q--10-”和许多其他非整数字符串。

最好正确定义integer。你可以这样做:

integer = Combine(Optional('-') + Word(nums))

但我发现对于这些在解析字符串中出现很多地方的低级表达式,Regex 是最好的:

integer = Regex(r"-?\d+") # Regex(r"-?[0-9]+") if you like more readable re's

然后为hex_integer也定义一个,

然后要添加宏,我们需要一个递归表达式,以处理宏的参数也是宏的可能性。

所以在这一点上,我们应该暂时停止编写代码,并进行一些设计。在解析器开发中,这种设计通常看起来像一个 BNF,您可以在其中用一种伪代码来描述您的解析器:

enum_expr ::= "typedef" "enum" [identifier] 
                "{" 
                    enum_item_list 
                "}" [identifier] ";"

enum_item_list ::= enum_item ["," enum_item]... [","]
enum_item ::= identifier "=" enum_value

enum_value ::= integer | hex_integer | macro_expression
macro_expression ::= identifier "(" enum_value ["," enum_value]... ")"

注意 macro_expression 的递归:它用于定义 enum_value,但它包含 enum_value 作为其自身定义的一部分。在pyparsing中,我们使用一个Forward来设置这种递归。

看看下面的代码是如何实现 BNF 的。我以您发布的一些项目为基础,但宏表达式需要一些返工。底线是“不要只是不断地向整数添加字符以试图让某些东西起作用。”

LBRACE, RBRACE, EQ, COMMA, LPAR, RPAR, SEMI = map(Suppress, "{}=,();")

_typedef = Keyword("typedef").suppress()
_enum = Keyword("enum").suppress()
identifier = Word(alphas, alphanums + "_")

# define an enumValue expression that is recursive, so that enumValues
# that are macros can take parameters that are enumValues
enumValue = Forward()

# add more types as needed - parse action on hex_integer will do parse-time
# conversion to int
integer = Regex(r"-?\d+").addParseAction(lambda t: int(t[0]))
# or just use the signed_integer expression found in pyparsing_common
# integer = pyparsing_common.signed_integer
hex_integer = Regex(r"0x[0-9a-fA-F]+").addParseAction(lambda t: int(t[0], 16))

# a macro defined using enumValue for parameters
macro_expr = Group(identifier + LPAR + Group(delimitedList(enumValue)) + RPAR)

# use '<<=' operator to attach recursive definition to enumValue
enumValue <<= hex_integer | integer | macro_expr

# remaining enum expressions
enumItem = Group(identifier("name") + Optional(EQ + enumValue("value")))
enumList = Group(delimitedList(enumItem) + Optional(COMMA))
enum = (_typedef
        + _enum
        + Optional(identifier("enum"))
        + LBRACE
        + enumList("names")
        + RBRACE
        + Optional(identifier("typedef"))
        + SEMI
        )

# this comment style includes cStyleComment too, so no need to
# ignore both
enum.ignore(cppStyleComment)

试试看:

enum.runTests([
    """
    typedef enum
    {
      VAL_1 = -1,
      VAL_2 =  0,
      VAL_3 = 0x10,
      VAL_4 = TEST_ENUM_CUSTOM(1,2)
    }MyENUM;
    """,
    ])

runTests 用于在开发期间测试和调试您的解析器。使用 enum.parseString(some_enum_expression)enum.searchString(some_c_header_file_text) 获取实际解析结果。

使用即将发布的 pyparsing 3.0 版本中的新铁路图功能,这是此解析器的可视化表示: